mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Parse custom NativeState in TypeScript (#34786)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/34786 This diff is the TS equivalent of D39686251. It introduces the possibility to parse a custom Native State in Typescript. The parsing follows the exact same rules as props, as initial heuristic. This should allow enough customization for the developers who needs a custom state. Currently, we only support using `interface` for the state and the interface must contain the `NativeState` string in its name. This diff introduces also tests for the TypeScript parser and it aligns the tests between Flow and TS. ## Changelog [General][Added] - Implement custom Native State parsing in TypeScript Reviewed By: cortinico Differential Revision: D39811476 fbshipit-source-id: 1e1b86b50b9632c13157ff6c8115f5ebcbada643
This commit is contained in:
committed by
Facebook GitHub Bot
parent
925b15351f
commit
bbb2fb212d
Vendored
+3
-12
@@ -14,20 +14,11 @@ const {compareSnaps, compareTsArraySnaps} = require('../compareSnaps.js');
|
||||
|
||||
const flowFixtures = require('../../flow/components/__test_fixtures__/fixtures.js');
|
||||
const flowSnaps = require('../../../../src/parsers/flow/components/__tests__/__snapshots__/component-parser-test.js.snap');
|
||||
const flowExtraCases = [
|
||||
//TODO: remove these once we implement TypeScript parser for Custom State
|
||||
'ALL_STATE_TYPES',
|
||||
'ARRAY_STATE_TYPES',
|
||||
'COMMANDS_EVENTS_STATE_TYPES_EXPORTED',
|
||||
'OBJECT_STATE_TYPES',
|
||||
];
|
||||
const flowExtraCases = [];
|
||||
const tsFixtures = require('../../typescript/components/__test_fixtures__/fixtures.js');
|
||||
const tsSnaps = require('../../../../src/parsers/typescript/components/__tests__/__snapshots__/typescript-component-parser-test.js.snap');
|
||||
const tsExtraCases = ['ARRAY2_PROP_TYPES_NO_EVENTS'].concat([
|
||||
//TODO: remove these once we implement TypeScript parser for Custom State
|
||||
'COMMANDS_AND_EVENTS_TYPES_EXPORTED',
|
||||
]);
|
||||
const ignoredCases = ['ARRAY_PROP_TYPES_NO_EVENTS'];
|
||||
const tsExtraCases = ['ARRAY2_PROP_TYPES_NO_EVENTS', 'ARRAY2_STATE_TYPES'];
|
||||
const ignoredCases = ['ARRAY_PROP_TYPES_NO_EVENTS', 'ARRAY_STATE_TYPES'];
|
||||
|
||||
compareSnaps(
|
||||
flowFixtures,
|
||||
|
||||
Vendored
+315
@@ -485,6 +485,310 @@ export default codegenNativeComponent<ModuleProps>(
|
||||
) as HostComponent<ModuleProps>;
|
||||
`;
|
||||
|
||||
// === STATE ===
|
||||
const NULLABLE_STATE_WITH_DEFAULT = `
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
|
||||
import type {WithDefault, Float} from 'CodegenTypes';
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
import type {HostComponent} from 'react-native';
|
||||
|
||||
export interface ModuleProps extends ViewProps { }
|
||||
|
||||
interface ModuleNativeState {
|
||||
nullable_with_default: WithDefault<Float, 1.0> | null | undefined;
|
||||
}
|
||||
|
||||
export default codegenNativeComponent<ModuleProps>(
|
||||
'Module',
|
||||
) as HostComponent<ModuleProps>;
|
||||
`;
|
||||
|
||||
const NON_OPTIONAL_KEY_STATE_WITH_DEFAULT_VALUE = `
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
|
||||
import type {WithDefault, Float} from 'CodegenTypes';
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
import type {HostComponent} from 'react-native';
|
||||
|
||||
export interface ModuleProps extends ViewProps {}
|
||||
|
||||
export interface ModuleNativeState {
|
||||
required_key_with_default: WithDefault<Float, 1.0>;
|
||||
}
|
||||
|
||||
export default codegenNativeComponent<ModuleProps>(
|
||||
'Module',
|
||||
) as HostComponent<ModuleProps>;
|
||||
`;
|
||||
|
||||
const STATE_CONFLICT_NAMES = `
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
import type {HostComponent} from 'react-native';
|
||||
|
||||
export interface ModuleProps extends ViewProps { }
|
||||
|
||||
interface ModuleNativeState {
|
||||
isEnabled: string,
|
||||
|
||||
isEnabled: boolean,
|
||||
}
|
||||
|
||||
export default codegenNativeComponent<ModuleProps>(
|
||||
'Module',
|
||||
) as HostComponent<ModuleProps>;
|
||||
`;
|
||||
|
||||
const STATE_CONFLICT_WITH_SPREAD_PROPS = `
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
import type {HostComponent} from 'react-native';
|
||||
|
||||
export interface ModuleProps extends ViewProps { }
|
||||
|
||||
type PropsInFile = Readonly<{
|
||||
isEnabled: boolean,
|
||||
}>;
|
||||
|
||||
export interface ModuleNativeState extends PropsInFile {
|
||||
isEnabled: boolean,
|
||||
}
|
||||
|
||||
export default codegenNativeComponent<ModuleProps>(
|
||||
'Module',
|
||||
) as HostComponent<ModuleProps>;
|
||||
`;
|
||||
|
||||
const STATE_NUMBER_TYPE = `
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
import type {HostComponent} from 'react-native';
|
||||
|
||||
export interface ModuleProps extends ViewProps { }
|
||||
|
||||
interface ModuleNativeState {
|
||||
someProp: number
|
||||
}
|
||||
|
||||
export default codegenNativeComponent<ModuleProps>(
|
||||
'Module',
|
||||
) as HostComponent<ModuleProps>;
|
||||
`;
|
||||
|
||||
const STATE_MIXED_ENUM = `
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
import type {HostComponent} from 'react-native';
|
||||
import type {WithDefault} from 'CodegenTypes';
|
||||
|
||||
export interface ModuleProps extends ViewProps { }
|
||||
|
||||
export interface ModuleNativeState {
|
||||
someProp?: WithDefault<'foo' | 1, 1>;
|
||||
}
|
||||
|
||||
export default codegenNativeComponent<ModuleProps>(
|
||||
'Module',
|
||||
) as HostComponent<ModuleProps>;
|
||||
`;
|
||||
|
||||
const STATE_ENUM_BOOLEAN = `
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
import type {HostComponent} from 'react-native';
|
||||
import type {WithDefault} from 'CodegenTypes';
|
||||
|
||||
export interface ModuleProps extends ViewProps { }
|
||||
|
||||
interface ModuleNativeState {
|
||||
someProp?: WithDefault<false | true, false>
|
||||
}
|
||||
|
||||
export default codegenNativeComponent<ModuleProps>(
|
||||
'Module',
|
||||
) as HostComponent<ModuleProps>;
|
||||
`;
|
||||
|
||||
const STATE_ARRAY_MIXED_ENUM = `
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
import type {HostComponent} from 'react-native';
|
||||
import type {WithDefault} from 'CodegenTypes';
|
||||
|
||||
export interface ModuleProps extends ViewProps { }
|
||||
|
||||
export interface ModuleNativeState {
|
||||
someProp?: WithDefault<ReadonlyArray<'foo' | 1>, 1>;
|
||||
}
|
||||
|
||||
export default codegenNativeComponent<ModuleProps>(
|
||||
'Module',
|
||||
) as HostComponent<ModuleProps>;
|
||||
`;
|
||||
|
||||
const STATE_ARRAY_ENUM_BOOLEAN = `
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
import type {HostComponent} from 'react-native';
|
||||
import type {WithDefault} from 'CodegenTypes';
|
||||
|
||||
export interface ModuleProps extends ViewProps { }
|
||||
|
||||
interface ModuleNativeState {
|
||||
someProp?: WithDefault<ReadonlyArray<false | true>, false>;
|
||||
}
|
||||
|
||||
export default codegenNativeComponent<ModuleProps>(
|
||||
'Module',
|
||||
) as HostComponent<ModuleProps>;
|
||||
`;
|
||||
|
||||
const STATE_ARRAY_ENUM_INT = `
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
import type {HostComponent} from 'react-native';
|
||||
import type {WithDefault} from 'CodegenTypes';
|
||||
|
||||
export interface ModuleProps extends ViewProps { }
|
||||
|
||||
export interface ModuleNativeState {
|
||||
someProp?: WithDefault<ReadonlyArray<0 | 1>, 0>;
|
||||
}
|
||||
|
||||
export default codegenNativeComponent<ModuleProps>(
|
||||
'Module',
|
||||
) as HostComponent<ModuleProps>;
|
||||
`;
|
||||
|
||||
const DOUBLE_STATE_IN_FILE = `
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
import type {HostComponent} from 'react-native';
|
||||
|
||||
export interface ModuleProps extends ViewProps { }
|
||||
|
||||
interface SecondNativeState {
|
||||
someProp: boolean
|
||||
}
|
||||
|
||||
export interface ModuleNativeState {
|
||||
someOtherProp: boolean
|
||||
}
|
||||
|
||||
export default codegenNativeComponent<ModuleProps>(
|
||||
'Module',
|
||||
) as HostComponent<ModuleProps>;
|
||||
`;
|
||||
|
||||
module.exports = {
|
||||
COMMANDS_DEFINED_INLINE,
|
||||
COMMANDS_DEFINED_MULTIPLE_TIMES,
|
||||
@@ -502,4 +806,15 @@ module.exports = {
|
||||
PROP_ARRAY_MIXED_ENUM,
|
||||
PROP_ARRAY_ENUM_BOOLEAN,
|
||||
PROP_ARRAY_ENUM_INT,
|
||||
NULLABLE_STATE_WITH_DEFAULT,
|
||||
NON_OPTIONAL_KEY_STATE_WITH_DEFAULT_VALUE,
|
||||
STATE_CONFLICT_NAMES,
|
||||
STATE_CONFLICT_WITH_SPREAD_PROPS,
|
||||
STATE_NUMBER_TYPE,
|
||||
STATE_MIXED_ENUM,
|
||||
STATE_ENUM_BOOLEAN,
|
||||
STATE_ARRAY_MIXED_ENUM,
|
||||
STATE_ARRAY_ENUM_BOOLEAN,
|
||||
STATE_ARRAY_ENUM_INT,
|
||||
DOUBLE_STATE_IN_FILE,
|
||||
};
|
||||
|
||||
Vendored
+532
-2
@@ -1034,7 +1034,7 @@ export default codegenNativeComponent<ModuleProps>('Module') as NativeType;
|
||||
|
||||
`;
|
||||
|
||||
const COMMANDS_AND_EVENTS_TYPES_EXPORTED = `
|
||||
const COMMANDS_EVENTS_STATE_TYPES_EXPORTED = `
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
@@ -1071,6 +1071,13 @@ export interface ModuleProps extends ViewProps {
|
||||
onDirectEventDefinedInlineWithPaperName: DirectEventHandler<EventInFile, 'paperDirectEventDefinedInlineWithPaperName'>,
|
||||
}
|
||||
|
||||
// Add state here
|
||||
export interface ModuleNativeState {
|
||||
boolean_required: boolean,
|
||||
boolean_optional_key?: WithDefault<boolean, true>,
|
||||
boolean_optional_both?: WithDefault<boolean, true>,
|
||||
}
|
||||
|
||||
type NativeType = HostComponent<ModuleProps>;
|
||||
|
||||
export type ScrollTo = (viewRef: React.ElementRef<NativeType>, y: Int, animated: Boolean) => Void;
|
||||
@@ -1088,6 +1095,525 @@ export default codegenNativeComponent<ModuleProps>(
|
||||
) as NativeType;
|
||||
`;
|
||||
|
||||
// === STATE ===
|
||||
const ALL_STATE_TYPES = `
|
||||
/**
|
||||
* 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';
|
||||
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
|
||||
import type {Int32, Double, Float, WithDefault} from 'CodegenTypes';
|
||||
import type {ImageSource} from 'ImageSource';
|
||||
import type {
|
||||
ColorValue,
|
||||
ColorArrayValue,
|
||||
PointValue,
|
||||
EdgeInsetsValue,
|
||||
} from 'StyleSheetTypes';
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
import type {HostComponent} from 'react-native';
|
||||
|
||||
export interface ModuleProps extends ViewProps { }
|
||||
|
||||
export interface ModuleNativeState {
|
||||
// Props
|
||||
// Boolean props
|
||||
boolean_required: boolean;
|
||||
boolean_optional_key?: WithDefault<boolean, true>;
|
||||
boolean_optional_both?: WithDefault<boolean, true>;
|
||||
|
||||
// Boolean props, null default
|
||||
boolean_null_optional_key?: WithDefault<boolean, null>;
|
||||
boolean_null_optional_both?: WithDefault<boolean, null>;
|
||||
|
||||
// String props
|
||||
string_required: string;
|
||||
string_optional_key?: WithDefault<string, ''>;
|
||||
string_optional_both?: WithDefault<string, ''>;
|
||||
|
||||
// String props, null default
|
||||
string_null_optional_key?: WithDefault<string, null>;
|
||||
string_null_optional_both?: WithDefault<string, null>;
|
||||
|
||||
// Stringish props
|
||||
stringish_required: Stringish;
|
||||
stringish_optional_key?: WithDefault<Stringish, ''>;
|
||||
stringish_optional_both?: WithDefault<Stringish, ''>;
|
||||
|
||||
// Stringish props, null default
|
||||
stringish_null_optional_key?: WithDefault<Stringish, null>;
|
||||
stringish_null_optional_both?: WithDefault<Stringish, null>;
|
||||
|
||||
// Double props
|
||||
double_required: Double;
|
||||
double_optional_key?: WithDefault<Double, 1.1>;
|
||||
double_optional_both?: WithDefault<Double, 1.1>;
|
||||
|
||||
// Float props
|
||||
float_required: Float;
|
||||
float_optional_key?: WithDefault<Float, 1.1>;
|
||||
float_optional_both?: WithDefault<Float, 1.1>;
|
||||
|
||||
// Float props, null default
|
||||
float_null_optional_key?: WithDefault<Float, null>;
|
||||
float_null_optional_both?: WithDefault<Float, null>;
|
||||
|
||||
// Int32 props
|
||||
int32_required: Int32;
|
||||
int32_optional_key?: WithDefault<Int32, 1>;
|
||||
int32_optional_both?: WithDefault<Int32, 1>;
|
||||
|
||||
// String enum props
|
||||
enum_optional_key?: WithDefault<'small' | 'large', 'small'>;
|
||||
enum_optional_both?: WithDefault<'small' | 'large', 'small'>;
|
||||
|
||||
// Int enum props
|
||||
int_enum_optional_key?: WithDefault<0 | 1, 0>;
|
||||
|
||||
// Object props
|
||||
object_optional_key?: Readonly<{prop: string}>;
|
||||
object_optional_both?: Readonly<{prop: string} | null | undefined>;
|
||||
object_optional_value: Readonly<{prop: string} | null | undefined>;
|
||||
|
||||
// ImageSource props
|
||||
image_required: ImageSource;
|
||||
image_optional_value: ImageSource | null | undefined;
|
||||
image_optional_both?: ImageSource | null | undefined;
|
||||
|
||||
// ColorValue props
|
||||
color_required: ColorValue;
|
||||
color_optional_key?: ColorValue;
|
||||
color_optional_value: ColorValue | null | undefined;
|
||||
color_optional_both?: ColorValue | null | undefined;
|
||||
|
||||
// ColorArrayValue props
|
||||
color_array_required: ColorArrayValue;
|
||||
color_array_optional_key?: ColorArrayValue;
|
||||
color_array_optional_value: ColorArrayValue | null | undefined;
|
||||
color_array_optional_both?: ColorArrayValue | null | undefined;
|
||||
|
||||
// ProcessedColorValue props
|
||||
processed_color_required: ProcessedColorValue;
|
||||
processed_color_optional_key?: ProcessedColorValue;
|
||||
processed_color_optional_value: ProcessedColorValue | null | undefined;
|
||||
processed_color_optional_both?: ProcessedColorValue | null | undefined;
|
||||
|
||||
// PointValue props
|
||||
point_required: PointValue;
|
||||
point_optional_key?: PointValue;
|
||||
point_optional_value: PointValue | null | undefined;
|
||||
point_optional_both?: PointValue | null | undefined;
|
||||
|
||||
// EdgeInsets props
|
||||
insets_required: EdgeInsetsValue;
|
||||
insets_optional_key?: EdgeInsetsValue;
|
||||
insets_optional_value: EdgeInsetsValue | null | undefined;
|
||||
insets_optional_both?: EdgeInsetsValue | null | undefined;
|
||||
}
|
||||
|
||||
export default codegenNativeComponent<ModuleProps>(
|
||||
'Module',
|
||||
) as HostComponent<ModuleProps>;
|
||||
`;
|
||||
|
||||
const ARRAY_STATE_TYPES = `
|
||||
/**
|
||||
* 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';
|
||||
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
|
||||
import type {Int32, Double, Float, WithDefault} from 'CodegenTypes';
|
||||
import type {ImageSource} from 'ImageSource';
|
||||
import type {
|
||||
ColorValue,
|
||||
ColorArrayValue,
|
||||
PointValue,
|
||||
EdgeInsetsValue,
|
||||
} from 'StyleSheetTypes';
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
import type {HostComponent} from 'react-native';
|
||||
|
||||
type ObjectType = Readonly<{prop: string}>;
|
||||
type ArrayObjectType = ReadonlyArray<Readonly<{prop: string}>>;
|
||||
|
||||
export interface ModuleProps extends ViewProps {}
|
||||
|
||||
interface ModuleNativeState {
|
||||
// Props
|
||||
// Boolean props
|
||||
array_boolean_required: ReadonlyArray<boolean>;
|
||||
array_boolean_optional_key?: ReadonlyArray<boolean>;
|
||||
array_boolean_optional_value: ReadonlyArray<boolean> | null | undefined;
|
||||
array_boolean_optional_both?: ReadonlyArray<boolean> | null | undefined;
|
||||
|
||||
// String props
|
||||
array_string_required: ReadonlyArray<string>;
|
||||
array_string_optional_key?: ReadonlyArray<string>;
|
||||
array_string_optional_value: ReadonlyArray<string> | null | undefined;
|
||||
array_string_optional_both?: ReadonlyArray<string> | null | undefined;
|
||||
|
||||
// Double props
|
||||
array_double_required: ReadonlyArray<Double>;
|
||||
array_double_optional_key?: ReadonlyArray<Double>;
|
||||
array_double_optional_value: ReadonlyArray<Double> | null | undefined;
|
||||
array_double_optional_both?: ReadonlyArray<Double> | null | undefined;
|
||||
|
||||
// Float props
|
||||
array_float_required: ReadonlyArray<Float>;
|
||||
array_float_optional_key?: ReadonlyArray<Float>;
|
||||
array_float_optional_value: ReadonlyArray<Float> | null | undefined;
|
||||
array_float_optional_both?: ReadonlyArray<Float> | null | undefined;
|
||||
|
||||
// Int32 props
|
||||
array_int32_required: ReadonlyArray<Int32>;
|
||||
array_int32_optional_key?: ReadonlyArray<Int32>;
|
||||
array_int32_optional_value: ReadonlyArray<Int32> | null | undefined;
|
||||
array_int32_optional_both?: ReadonlyArray<Int32> | null | undefined;
|
||||
|
||||
// String enum props
|
||||
array_enum_optional_key?: WithDefault<
|
||||
ReadonlyArray<'small' | 'large'>,
|
||||
'small'
|
||||
>;
|
||||
array_enum_optional_both?: WithDefault<
|
||||
ReadonlyArray<'small' | 'large'>,
|
||||
'small'
|
||||
>;
|
||||
|
||||
// ImageSource props
|
||||
array_image_required: ReadonlyArray<ImageSource>;
|
||||
array_image_optional_key?: ReadonlyArray<ImageSource>;
|
||||
array_image_optional_value: ReadonlyArray<ImageSource> | null | undefined;
|
||||
array_image_optional_both?: ReadonlyArray<ImageSource> | null | undefined;
|
||||
|
||||
// ColorValue props
|
||||
array_color_required: ReadonlyArray<ColorValue>;
|
||||
array_color_optional_key?: ReadonlyArray<ColorValue>;
|
||||
array_color_optional_value: ReadonlyArray<ColorValue> | null | undefined;
|
||||
array_color_optional_both?: ReadonlyArray<ColorValue> | null | undefined;
|
||||
|
||||
// PointValue props
|
||||
array_point_required: ReadonlyArray<PointValue>;
|
||||
array_point_optional_key?: ReadonlyArray<PointValue>;
|
||||
array_point_optional_value: ReadonlyArray<PointValue> | null | undefined;
|
||||
array_point_optional_both?: ReadonlyArray<PointValue> | null | undefined;
|
||||
|
||||
// EdgeInsetsValue props
|
||||
array_insets_required: ReadonlyArray<EdgeInsetsValue>;
|
||||
array_insets_optional_key?: ReadonlyArray<EdgeInsetsValue>;
|
||||
array_insets_optional_value: ReadonlyArray<EdgeInsetsValue> | null | undefined;
|
||||
array_insets_optional_both?: ReadonlyArray<EdgeInsetsValue> | null | undefined;
|
||||
|
||||
// Object props
|
||||
array_object_required: ReadonlyArray<Readonly<{prop: string}>>;
|
||||
array_object_optional_key?: ReadonlyArray<Readonly<{prop: string}>>;
|
||||
array_object_optional_value: ArrayObjectType | null | undefined;
|
||||
array_object_optional_both?: ReadonlyArray<ObjectType> | null | undefined;
|
||||
|
||||
// Nested array object types
|
||||
array_of_array_object_required: ReadonlyArray<
|
||||
Readonly<{
|
||||
// This needs to be the same name as the top level array above
|
||||
array_object_required: ReadonlyArray<Readonly<{prop: string}>>;
|
||||
}>
|
||||
>;
|
||||
array_of_array_object_optional_key?: ReadonlyArray<
|
||||
Readonly<{
|
||||
// This needs to be the same name as the top level array above
|
||||
array_object_optional_key: ReadonlyArray<Readonly<{prop?: string}>>;
|
||||
}>
|
||||
>;
|
||||
array_of_array_object_optional_value: ReadonlyArray<
|
||||
Readonly<{
|
||||
// This needs to be the same name as the top level array above
|
||||
array_object_optional_value: ReadonlyArray<
|
||||
Readonly<{prop: string | null | undefined}>
|
||||
>;
|
||||
}>
|
||||
> | null | undefined;
|
||||
array_of_array_object_optional_both?: ReadonlyArray<
|
||||
Readonly<{
|
||||
// This needs to be the same name as the top level array above
|
||||
array_object_optional_both: ReadonlyArray<
|
||||
Readonly<{prop?: string | null | undefined}>
|
||||
>;
|
||||
}>
|
||||
> | null | undefined;
|
||||
|
||||
// Nested array of array of object types
|
||||
array_of_array_of_object_required: ReadonlyArray<
|
||||
ReadonlyArray<
|
||||
Readonly<{
|
||||
prop: string;
|
||||
}>
|
||||
>
|
||||
>;
|
||||
|
||||
// Nested array of array of object types (in file)
|
||||
array_of_array_of_object_required_in_file: ReadonlyArray<
|
||||
ReadonlyArray<ObjectType>
|
||||
>;
|
||||
}
|
||||
|
||||
export default codegenNativeComponent<ModuleProps>(
|
||||
'Module',
|
||||
) as HostComponent<ModuleProps>;
|
||||
`;
|
||||
|
||||
const ARRAY2_STATE_TYPES = `
|
||||
/**
|
||||
* 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';
|
||||
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
|
||||
import type {Int32, Double, Float, WithDefault} from 'CodegenTypes';
|
||||
import type {ImageSource} from 'ImageSource';
|
||||
import type {
|
||||
ColorValue,
|
||||
ColorArrayValue,
|
||||
PointValue,
|
||||
EdgeInsetsValue,
|
||||
} from 'StyleSheetTypes';
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
import type {HostComponent} from 'react-native';
|
||||
|
||||
type ObjectType = Readonly<{prop: string}>;
|
||||
type ArrayObjectType = readonly Readonly<{prop: string}>[];
|
||||
|
||||
export interface ModuleProps extends ViewProps {}
|
||||
|
||||
export interface ModuleNativeState{
|
||||
// Props
|
||||
// Boolean props
|
||||
array_boolean_required: readonly boolean[];
|
||||
array_boolean_optional_key?: readonly boolean[];
|
||||
array_boolean_optional_value: readonly boolean[] | null | undefined;
|
||||
array_boolean_optional_both?: readonly boolean[] | null | undefined;
|
||||
|
||||
// String props
|
||||
array_string_required: readonly string[];
|
||||
array_string_optional_key?: readonly string[];
|
||||
array_string_optional_value: readonly string[] | null | undefined;
|
||||
array_string_optional_both?: readonly string[] | null | undefined;
|
||||
|
||||
// Double props
|
||||
array_double_required: readonly Double[];
|
||||
array_double_optional_key?: readonly Double[];
|
||||
array_double_optional_value: readonly Double[] | null | undefined;
|
||||
array_double_optional_both?: readonly Double[] | null | undefined;
|
||||
|
||||
// Float props
|
||||
array_float_required: readonly Float[];
|
||||
array_float_optional_key?: readonly Float[];
|
||||
array_float_optional_value: readonly Float[] | null | undefined;
|
||||
array_float_optional_both?: readonly Float[] | null | undefined;
|
||||
|
||||
// Int32 props
|
||||
array_int32_required: readonly Int32[];
|
||||
array_int32_optional_key?: readonly Int32[];
|
||||
array_int32_optional_value: readonly Int32[] | null | undefined;
|
||||
array_int32_optional_both?: readonly Int32[] | null | undefined;
|
||||
|
||||
// String enum props
|
||||
array_enum_optional_key?: WithDefault<
|
||||
readonly ('small' | 'large')[],
|
||||
'small'
|
||||
>;
|
||||
array_enum_optional_both?: WithDefault<
|
||||
readonly ('small' | 'large')[],
|
||||
'small'
|
||||
>;
|
||||
|
||||
// ImageSource props
|
||||
array_image_required: readonly ImageSource[];
|
||||
array_image_optional_key?: readonly ImageSource[];
|
||||
array_image_optional_value: readonly ImageSource[] | null | undefined;
|
||||
array_image_optional_both?: readonly ImageSource[] | null | undefined;
|
||||
|
||||
// ColorValue props
|
||||
array_color_required: readonly ColorValue[];
|
||||
array_color_optional_key?: readonly ColorValue[];
|
||||
array_color_optional_value: readonly ColorValue[] | null | undefined;
|
||||
array_color_optional_both?: readonly ColorValue[] | null | undefined;
|
||||
|
||||
// PointValue props
|
||||
array_point_required: readonly PointValue[];
|
||||
array_point_optional_key?: readonly PointValue[];
|
||||
array_point_optional_value: readonly PointValue[] | null | undefined;
|
||||
array_point_optional_both?: readonly PointValue[] | null | undefined;
|
||||
|
||||
// EdgeInsetsValue props
|
||||
array_insets_required: readonly EdgeInsetsValue[];
|
||||
array_insets_optional_key?: readonly EdgeInsetsValue[];
|
||||
array_insets_optional_value: readonly EdgeInsetsValue[] | null | undefined;
|
||||
array_insets_optional_both?: readonly EdgeInsetsValue[] | null | undefined;
|
||||
|
||||
// Object props
|
||||
array_object_required: readonly Readonly<{prop: string}>[];
|
||||
array_object_optional_key?: readonly Readonly<{prop: string}>[];
|
||||
array_object_optional_value: ArrayObjectType | null | undefined;
|
||||
array_object_optional_both?: readonly ObjectType[] | null | undefined;
|
||||
|
||||
// Nested array object types
|
||||
array_of_array_object_required: readonly Readonly<{
|
||||
// This needs to be the same name as the top level array above
|
||||
array_object_required: readonly Readonly<{prop: string}>[];
|
||||
}>[];
|
||||
array_of_array_object_optional_key?: readonly Readonly<{
|
||||
// This needs to be the same name as the top level array above
|
||||
array_object_optional_key: readonly Readonly<{prop?: string}>[];
|
||||
}>[];
|
||||
array_of_array_object_optional_value: readonly Readonly<{
|
||||
// This needs to be the same name as the top level array above
|
||||
array_object_optional_value: readonly Readonly<{prop: string | null | undefined}>[];
|
||||
}>[] | null | undefined;
|
||||
array_of_array_object_optional_both?: readonly Readonly<{
|
||||
// This needs to be the same name as the top level array above
|
||||
array_object_optional_both: readonly Readonly<{prop?: string | null | undefined}>[];
|
||||
}>[] | null | undefined;
|
||||
|
||||
// Nested array of array of object types
|
||||
array_of_array_of_object_required: readonly Readonly<{
|
||||
prop: string;
|
||||
}>[][];
|
||||
|
||||
// Nested array of array of object types (in file)
|
||||
array_of_array_of_object_required_in_file: readonly ObjectType[][];
|
||||
}
|
||||
|
||||
export default codegenNativeComponent<ModuleProps>(
|
||||
'Module',
|
||||
) as HostComponent<ModuleProps>;
|
||||
`;
|
||||
|
||||
const OBJECT_STATE_TYPES = `
|
||||
/**
|
||||
* 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';
|
||||
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
|
||||
import type {Int32, Double, Float, WithDefault} from 'CodegenTypes';
|
||||
import type {ImageSource} from 'ImageSource';
|
||||
import type {
|
||||
ColorValue,
|
||||
ColorArrayValue,
|
||||
PointValue,
|
||||
EdgeInsetsValue,
|
||||
} from 'StyleSheetTypes';
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
import type {HostComponent} from 'react-native';
|
||||
|
||||
export interface ModuleProps extends ViewProps {}
|
||||
|
||||
interface ModuleNativeState {
|
||||
// Props
|
||||
// Boolean props
|
||||
boolean_required: Readonly<{prop: boolean}>;
|
||||
boolean_optional: Readonly<{prop?: WithDefault<boolean, false>}>;
|
||||
|
||||
// String props
|
||||
string_required: Readonly<{prop: string}>;
|
||||
string_optional: Readonly<{prop?: WithDefault<string, ''>}>;
|
||||
|
||||
// Double props
|
||||
double_required: Readonly<{prop: Double}>;
|
||||
double_optional: Readonly<{prop?: WithDefault<Double, 0.0>}>;
|
||||
|
||||
// Float props
|
||||
float_required: Readonly<{prop: Float}>;
|
||||
float_optional: Readonly<{prop?: WithDefault<Float, 0.0>}>;
|
||||
|
||||
// Int32 props
|
||||
int_required: Readonly<{prop: Int32}>;
|
||||
int_optional: Readonly<{prop?: WithDefault<Int32, 0>}>;
|
||||
|
||||
// String enum props
|
||||
enum_optional: Readonly<{
|
||||
prop?: WithDefault<ReadonlyArray<'small' | 'large'>, 'small'>;
|
||||
}>;
|
||||
|
||||
// ImageSource props
|
||||
image_required: Readonly<{prop: ImageSource}>;
|
||||
image_optional_key: Readonly<{prop?: ImageSource}>;
|
||||
image_optional_value: Readonly<{prop: ImageSource | null | undefined}>;
|
||||
image_optional_both: Readonly<{prop?: ImageSource | null | undefined}>;
|
||||
|
||||
// ColorValue props
|
||||
color_required: Readonly<{prop: ColorValue}>;
|
||||
color_optional_key: Readonly<{prop?: ColorValue}>;
|
||||
color_optional_value: Readonly<{prop: ColorValue | null | undefined}>;
|
||||
color_optional_both: Readonly<{prop?: ColorValue | null | undefined}>;
|
||||
|
||||
// ProcessedColorValue props
|
||||
processed_color_required: Readonly<{prop: ProcessedColorValue}>;
|
||||
processed_color_optional_key: Readonly<{prop?: ProcessedColorValue}>;
|
||||
processed_color_optional_value: Readonly<{
|
||||
prop: ProcessedColorValue | null | undefined;
|
||||
}>;
|
||||
processed_color_optional_both: Readonly<{
|
||||
prop?: ProcessedColorValue | null | undefined;
|
||||
}>;
|
||||
|
||||
// PointValue props
|
||||
point_required: Readonly<{prop: PointValue}>;
|
||||
point_optional_key: Readonly<{prop?: PointValue}>;
|
||||
point_optional_value: Readonly<{prop: PointValue | null | undefined}>;
|
||||
point_optional_both: Readonly<{prop?: PointValue | null | undefined}>;
|
||||
|
||||
// EdgeInsetsValue props
|
||||
insets_required: Readonly<{prop: EdgeInsetsValue}>;
|
||||
insets_optional_key: Readonly<{prop?: EdgeInsetsValue}>;
|
||||
insets_optional_value: Readonly<{prop: EdgeInsetsValue | null | undefined}>;
|
||||
insets_optional_both: Readonly<{prop?: EdgeInsetsValue | null | undefined}>;
|
||||
|
||||
// Nested object props
|
||||
object_required: Readonly<{prop: Readonly<{nestedProp: string}>}>;
|
||||
object_optional_key?: Readonly<{prop: Readonly<{nestedProp: string}>}>;
|
||||
object_optional_value: Readonly<{
|
||||
prop: Readonly<{nestedProp: string}>;
|
||||
}> | null | undefined;
|
||||
object_optional_both?: Readonly<{
|
||||
prop: Readonly<{nestedProp: string}>;
|
||||
}> | null | undefined;
|
||||
}
|
||||
|
||||
export default codegenNativeComponent<ModuleProps>(
|
||||
'Module',
|
||||
) as HostComponent<ModuleProps>;
|
||||
`;
|
||||
|
||||
module.exports = {
|
||||
ALL_PROP_TYPES_NO_EVENTS,
|
||||
ARRAY_PROP_TYPES_NO_EVENTS,
|
||||
@@ -1100,8 +1626,12 @@ module.exports = {
|
||||
EVENTS_DEFINED_INLINE_WITH_ALL_TYPES,
|
||||
EVENTS_DEFINED_AS_NULL_INLINE,
|
||||
PROPS_AND_EVENTS_TYPES_EXPORTED,
|
||||
COMMANDS_AND_EVENTS_TYPES_EXPORTED,
|
||||
COMMANDS_EVENTS_STATE_TYPES_EXPORTED,
|
||||
COMMANDS_DEFINED_WITH_ALL_TYPES,
|
||||
PROPS_AS_EXTERNAL_TYPES,
|
||||
COMMANDS_WITH_EXTERNAL_TYPES,
|
||||
ALL_STATE_TYPES,
|
||||
ARRAY_STATE_TYPES,
|
||||
ARRAY2_STATE_TYPES,
|
||||
OBJECT_STATE_TYPES,
|
||||
};
|
||||
|
||||
+2687
-90
File diff suppressed because it is too large
Load Diff
+693
@@ -0,0 +1,693 @@
|
||||
/**
|
||||
* 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-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
import type {ASTNode} from '../utils';
|
||||
import type {TypeDeclarationMap} from '../utils.js';
|
||||
import type {NamedShape} from '../../../CodegenSchema.js';
|
||||
const {getValueFromTypes} = require('../utils.js');
|
||||
|
||||
function getProperties(
|
||||
typeName: string,
|
||||
types: TypeDeclarationMap,
|
||||
): $FlowFixMe {
|
||||
const alias = types[typeName];
|
||||
if (!alias) {
|
||||
throw new Error(
|
||||
`Failed to find definition for "${typeName}", please check that you have a valid codegen typescript file`,
|
||||
);
|
||||
}
|
||||
const aliasKind =
|
||||
alias.type === 'TSInterfaceDeclaration' ? 'interface' : 'type';
|
||||
|
||||
try {
|
||||
if (aliasKind === 'interface') {
|
||||
return [...(alias.extends ?? []), ...alias.body.body];
|
||||
}
|
||||
|
||||
return (
|
||||
alias.typeAnnotation.members ||
|
||||
alias.typeAnnotation.typeParameters.params[0].members ||
|
||||
alias.typeAnnotation.typeParameters.params
|
||||
);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Failed to find ${aliasKind} definition for "${typeName}", please check that you have a valid codegen typescript file`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function getTypeAnnotationForObjectAsArrayElement<T>(
|
||||
objectType: $FlowFixMe,
|
||||
types: TypeDeclarationMap,
|
||||
buildSchema: (property: PropAST, types: TypeDeclarationMap) => ?NamedShape<T>,
|
||||
): $FlowFixMe {
|
||||
return {
|
||||
type: 'ObjectTypeAnnotation',
|
||||
properties: flattenProperties(
|
||||
objectType.typeParameters.params[0].members ||
|
||||
objectType.typeParameters.params,
|
||||
types,
|
||||
)
|
||||
.map(prop => buildSchema(prop, types))
|
||||
.filter(Boolean),
|
||||
};
|
||||
}
|
||||
|
||||
function getTypeAnnotationForArrayOfArrayOfObject<T>(
|
||||
typeAnnotation: $FlowFixMe,
|
||||
types: TypeDeclarationMap,
|
||||
buildSchema: (property: PropAST, types: TypeDeclarationMap) => ?NamedShape<T>,
|
||||
): $FlowFixMe {
|
||||
// We need to go yet another level deeper to resolve
|
||||
// types that may be defined in a type alias
|
||||
const nestedObjectType = getValueFromTypes(typeAnnotation, types);
|
||||
|
||||
return {
|
||||
type: 'ArrayTypeAnnotation',
|
||||
elementType: getTypeAnnotationForObjectAsArrayElement(
|
||||
nestedObjectType,
|
||||
types,
|
||||
buildSchema,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
function getTypeAnnotationForArray<T>(
|
||||
name: string,
|
||||
typeAnnotation: $FlowFixMe,
|
||||
defaultValue: $FlowFixMe | null,
|
||||
types: TypeDeclarationMap,
|
||||
buildSchema: (property: PropAST, types: TypeDeclarationMap) => ?NamedShape<T>,
|
||||
): $FlowFixMe {
|
||||
if (typeAnnotation.type === 'TSParenthesizedType') {
|
||||
return getTypeAnnotationForArray(
|
||||
name,
|
||||
typeAnnotation.typeAnnotation,
|
||||
defaultValue,
|
||||
types,
|
||||
buildSchema,
|
||||
);
|
||||
}
|
||||
|
||||
const extractedTypeAnnotation = getValueFromTypes(typeAnnotation, types);
|
||||
|
||||
if (
|
||||
extractedTypeAnnotation.type === 'TSUnionType' &&
|
||||
extractedTypeAnnotation.types.some(
|
||||
t => t.type === 'TSNullKeyword' || t.type === 'TSUndefinedKeyword',
|
||||
)
|
||||
) {
|
||||
throw new Error(
|
||||
'Nested optionals such as "ReadonlyArray<boolean | null | undefined>" are not supported, please declare optionals at the top level of value definitions as in "ReadonlyArray<boolean> | null | undefined"',
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
extractedTypeAnnotation.type === 'TSTypeReference' &&
|
||||
extractedTypeAnnotation.typeName.name === 'WithDefault'
|
||||
) {
|
||||
throw new Error(
|
||||
'Nested defaults such as "ReadonlyArray<WithDefault<boolean, false>>" are not supported, please declare defaults at the top level of value definitions as in "WithDefault<ReadonlyArray<boolean>, false>"',
|
||||
);
|
||||
}
|
||||
|
||||
// Covers: T[]
|
||||
if (typeAnnotation.type === 'TSArrayType') {
|
||||
return getTypeAnnotationForArrayOfArrayOfObject(
|
||||
typeAnnotation.elementType,
|
||||
types,
|
||||
buildSchema,
|
||||
);
|
||||
}
|
||||
|
||||
if (extractedTypeAnnotation.type === 'TSTypeReference') {
|
||||
// Resolve the type alias if it's not defined inline
|
||||
const objectType = getValueFromTypes(extractedTypeAnnotation, types);
|
||||
|
||||
if (objectType.typeName.name === 'Readonly') {
|
||||
return getTypeAnnotationForObjectAsArrayElement(
|
||||
objectType,
|
||||
types,
|
||||
buildSchema,
|
||||
);
|
||||
}
|
||||
|
||||
// Covers: ReadonlyArray<T>
|
||||
if (objectType.typeName.name === 'ReadonlyArray') {
|
||||
return getTypeAnnotationForArrayOfArrayOfObject(
|
||||
objectType.typeParameters.params[0],
|
||||
types,
|
||||
buildSchema,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const type =
|
||||
extractedTypeAnnotation.elementType === 'TSTypeReference'
|
||||
? extractedTypeAnnotation.elementType.typeName.name
|
||||
: extractedTypeAnnotation.elementType?.type ||
|
||||
extractedTypeAnnotation.typeName?.name ||
|
||||
extractedTypeAnnotation.type;
|
||||
|
||||
switch (type) {
|
||||
case 'TSNumberKeyword':
|
||||
return {
|
||||
type: 'FloatTypeAnnotation',
|
||||
};
|
||||
case 'ImageSource':
|
||||
return {
|
||||
type: 'ReservedPropTypeAnnotation',
|
||||
name: 'ImageSourcePrimitive',
|
||||
};
|
||||
case 'ColorValue':
|
||||
case 'ProcessedColorValue':
|
||||
return {
|
||||
type: 'ReservedPropTypeAnnotation',
|
||||
name: 'ColorPrimitive',
|
||||
};
|
||||
case 'PointValue':
|
||||
return {
|
||||
type: 'ReservedPropTypeAnnotation',
|
||||
name: 'PointPrimitive',
|
||||
};
|
||||
case 'EdgeInsetsValue':
|
||||
return {
|
||||
type: 'ReservedPropTypeAnnotation',
|
||||
name: 'EdgeInsetsPrimitive',
|
||||
};
|
||||
case 'Stringish':
|
||||
return {
|
||||
type: 'StringTypeAnnotation',
|
||||
};
|
||||
case 'Int32':
|
||||
return {
|
||||
type: 'Int32TypeAnnotation',
|
||||
};
|
||||
case 'Double':
|
||||
return {
|
||||
type: 'DoubleTypeAnnotation',
|
||||
};
|
||||
case 'Float':
|
||||
return {
|
||||
type: 'FloatTypeAnnotation',
|
||||
};
|
||||
case 'TSBooleanKeyword':
|
||||
return {
|
||||
type: 'BooleanTypeAnnotation',
|
||||
};
|
||||
case 'TSStringKeyword':
|
||||
return {
|
||||
type: 'StringTypeAnnotation',
|
||||
};
|
||||
case 'TSUnionType':
|
||||
typeAnnotation.types.reduce((lastType, currType) => {
|
||||
const lastFlattenedType =
|
||||
lastType && lastType.type === 'TSLiteralType'
|
||||
? lastType.literal.type
|
||||
: lastType.type;
|
||||
const currFlattenedType =
|
||||
currType.type === 'TSLiteralType'
|
||||
? currType.literal.type
|
||||
: currType.type;
|
||||
|
||||
if (lastFlattenedType && currFlattenedType !== lastFlattenedType) {
|
||||
throw new Error(`Mixed types are not supported (see "${name}")`);
|
||||
}
|
||||
return currType;
|
||||
});
|
||||
|
||||
if (defaultValue === null) {
|
||||
throw new Error(`A default enum value is required for "${name}"`);
|
||||
}
|
||||
|
||||
const unionType = typeAnnotation.types[0].type;
|
||||
if (
|
||||
unionType === 'TSLiteralType' &&
|
||||
typeAnnotation.types[0].literal?.type === 'StringLiteral'
|
||||
) {
|
||||
return {
|
||||
type: 'StringEnumTypeAnnotation',
|
||||
default: (defaultValue: string),
|
||||
options: typeAnnotation.types.map(option => option.literal.value),
|
||||
};
|
||||
} else if (
|
||||
unionType === 'TSLiteralType' &&
|
||||
typeAnnotation.types[0].literal?.type === 'NumericLiteral'
|
||||
) {
|
||||
throw new Error(
|
||||
`Arrays of int enums are not supported (see: "${name}")`,
|
||||
);
|
||||
} else {
|
||||
throw new Error(
|
||||
`Unsupported union type for "${name}", received "${
|
||||
unionType === 'TSLiteralType'
|
||||
? typeAnnotation.types[0].literal?.type
|
||||
: unionType
|
||||
}"`,
|
||||
);
|
||||
}
|
||||
default:
|
||||
(type: empty);
|
||||
throw new Error(`Unknown prop type for "${name}": ${type}`);
|
||||
}
|
||||
}
|
||||
|
||||
function getTypeAnnotation<T>(
|
||||
name: string,
|
||||
annotation: $FlowFixMe | ASTNode,
|
||||
defaultValue: $FlowFixMe | null,
|
||||
withNullDefault: boolean,
|
||||
types: TypeDeclarationMap,
|
||||
buildSchema: (property: PropAST, types: TypeDeclarationMap) => ?NamedShape<T>,
|
||||
): $FlowFixMe {
|
||||
const typeAnnotation = getValueFromTypes(annotation, types);
|
||||
|
||||
// Covers: (T)
|
||||
if (typeAnnotation.type === 'TSParenthesizedType') {
|
||||
return getTypeAnnotation(
|
||||
name,
|
||||
typeAnnotation.typeAnnotation,
|
||||
defaultValue,
|
||||
withNullDefault,
|
||||
types,
|
||||
buildSchema,
|
||||
);
|
||||
}
|
||||
|
||||
// Covers: readonly T[]
|
||||
if (
|
||||
typeAnnotation.type === 'TSTypeOperator' &&
|
||||
typeAnnotation.operator === 'readonly' &&
|
||||
typeAnnotation.typeAnnotation.type === 'TSArrayType'
|
||||
) {
|
||||
return {
|
||||
type: 'ArrayTypeAnnotation',
|
||||
elementType: getTypeAnnotationForArray(
|
||||
name,
|
||||
typeAnnotation.typeAnnotation.elementType,
|
||||
defaultValue,
|
||||
types,
|
||||
buildSchema,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
// Covers: ReadonlyArray<T>
|
||||
if (
|
||||
typeAnnotation.type === 'TSTypeReference' &&
|
||||
typeAnnotation.typeName.name === 'ReadonlyArray'
|
||||
) {
|
||||
return {
|
||||
type: 'ArrayTypeAnnotation',
|
||||
elementType: getTypeAnnotationForArray(
|
||||
name,
|
||||
typeAnnotation.typeParameters.params[0],
|
||||
defaultValue,
|
||||
types,
|
||||
buildSchema,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
// Covers: Readonly<T[]>
|
||||
if (
|
||||
typeAnnotation.type === 'TSTypeReference' &&
|
||||
typeAnnotation.typeName?.name === 'Readonly' &&
|
||||
typeAnnotation.typeParameters.type === 'TSTypeParameterInstantiation' &&
|
||||
typeAnnotation.typeParameters.params[0].type === 'TSArrayType'
|
||||
) {
|
||||
return {
|
||||
type: 'ArrayTypeAnnotation',
|
||||
elementType: getTypeAnnotationForArray(
|
||||
name,
|
||||
typeAnnotation.typeParameters.params[0],
|
||||
defaultValue,
|
||||
types,
|
||||
buildSchema,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
(typeAnnotation.type === 'TSTypeReference' ||
|
||||
typeAnnotation.type === 'TSTypeLiteral') &&
|
||||
typeAnnotation.typeName?.name === 'Readonly'
|
||||
) {
|
||||
const rawProperties =
|
||||
typeAnnotation.typeParameters.params[0].members ||
|
||||
(typeAnnotation.typeParameters.params[0].types &&
|
||||
typeAnnotation.typeParameters.params[0].types[0].members) ||
|
||||
typeAnnotation.typeParameters.params;
|
||||
|
||||
const flattenedProperties = flattenProperties(rawProperties, types);
|
||||
|
||||
const properties = flattenedProperties
|
||||
.map(prop => buildSchema(prop, types))
|
||||
.filter(Boolean);
|
||||
|
||||
return {
|
||||
type: 'ObjectTypeAnnotation',
|
||||
properties,
|
||||
};
|
||||
}
|
||||
|
||||
const type =
|
||||
typeAnnotation.type === 'TSTypeReference' ||
|
||||
typeAnnotation.type === 'TSTypeAliasDeclaration'
|
||||
? typeAnnotation.typeName.name
|
||||
: typeAnnotation.type;
|
||||
|
||||
switch (type) {
|
||||
case 'ImageSource':
|
||||
return {
|
||||
type: 'ReservedPropTypeAnnotation',
|
||||
name: 'ImageSourcePrimitive',
|
||||
};
|
||||
case 'ColorValue':
|
||||
case 'ProcessedColorValue':
|
||||
return {
|
||||
type: 'ReservedPropTypeAnnotation',
|
||||
name: 'ColorPrimitive',
|
||||
};
|
||||
case 'ColorArrayValue':
|
||||
return {
|
||||
type: 'ArrayTypeAnnotation',
|
||||
elementType: {
|
||||
type: 'ReservedPropTypeAnnotation',
|
||||
name: 'ColorPrimitive',
|
||||
},
|
||||
};
|
||||
case 'PointValue':
|
||||
return {
|
||||
type: 'ReservedPropTypeAnnotation',
|
||||
name: 'PointPrimitive',
|
||||
};
|
||||
case 'EdgeInsetsValue':
|
||||
return {
|
||||
type: 'ReservedPropTypeAnnotation',
|
||||
name: 'EdgeInsetsPrimitive',
|
||||
};
|
||||
case 'Int32':
|
||||
return {
|
||||
type: 'Int32TypeAnnotation',
|
||||
default: ((defaultValue ? defaultValue : 0): number),
|
||||
};
|
||||
case 'Double':
|
||||
return {
|
||||
type: 'DoubleTypeAnnotation',
|
||||
default: ((defaultValue ? defaultValue : 0): number),
|
||||
};
|
||||
case 'Float':
|
||||
return {
|
||||
type: 'FloatTypeAnnotation',
|
||||
default: withNullDefault
|
||||
? (defaultValue: number | null)
|
||||
: ((defaultValue ? defaultValue : 0): number),
|
||||
};
|
||||
case 'TSBooleanKeyword':
|
||||
return {
|
||||
type: 'BooleanTypeAnnotation',
|
||||
default: withNullDefault
|
||||
? (defaultValue: boolean | null)
|
||||
: ((defaultValue == null ? false : defaultValue): boolean),
|
||||
};
|
||||
case 'TSStringKeyword':
|
||||
if (typeof defaultValue !== 'undefined') {
|
||||
return {
|
||||
type: 'StringTypeAnnotation',
|
||||
default: (defaultValue: string | null),
|
||||
};
|
||||
}
|
||||
throw new Error(`A default string (or null) is required for "${name}"`);
|
||||
case 'Stringish':
|
||||
if (typeof defaultValue !== 'undefined') {
|
||||
return {
|
||||
type: 'StringTypeAnnotation',
|
||||
default: (defaultValue: string | null),
|
||||
};
|
||||
}
|
||||
throw new Error(`A default string (or null) is required for "${name}"`);
|
||||
case 'TSUnionType':
|
||||
typeAnnotation.types.reduce((lastType, currType) => {
|
||||
const lastFlattenedType =
|
||||
lastType && lastType.type === 'TSLiteralType'
|
||||
? lastType.literal.type
|
||||
: lastType.type;
|
||||
const currFlattenedType =
|
||||
currType.type === 'TSLiteralType'
|
||||
? currType.literal.type
|
||||
: currType.type;
|
||||
|
||||
if (lastFlattenedType && currFlattenedType !== lastFlattenedType) {
|
||||
throw new Error(`Mixed types are not supported (see "${name}")`);
|
||||
}
|
||||
return currType;
|
||||
});
|
||||
|
||||
if (defaultValue === null) {
|
||||
throw new Error(`A default enum value is required for "${name}"`);
|
||||
}
|
||||
|
||||
const unionType = typeAnnotation.types[0].type;
|
||||
if (
|
||||
unionType === 'TSLiteralType' &&
|
||||
typeAnnotation.types[0].literal?.type === 'StringLiteral'
|
||||
) {
|
||||
return {
|
||||
type: 'StringEnumTypeAnnotation',
|
||||
default: (defaultValue: string),
|
||||
options: typeAnnotation.types.map(option => option.literal.value),
|
||||
};
|
||||
} else if (
|
||||
unionType === 'TSLiteralType' &&
|
||||
typeAnnotation.types[0].literal?.type === 'NumericLiteral'
|
||||
) {
|
||||
return {
|
||||
type: 'Int32EnumTypeAnnotation',
|
||||
default: (defaultValue: number),
|
||||
options: typeAnnotation.types.map(option => option.literal.value),
|
||||
};
|
||||
} else {
|
||||
throw new Error(
|
||||
`Unsupported union type for "${name}", received "${
|
||||
unionType === 'TSLiteralType'
|
||||
? typeAnnotation.types[0].literal?.type
|
||||
: unionType
|
||||
}"`,
|
||||
);
|
||||
}
|
||||
case 'TSNumberKeyword':
|
||||
throw new Error(
|
||||
`Cannot use "${type}" type annotation for "${name}": must use a specific numeric type like Int32, Double, or Float`,
|
||||
);
|
||||
default:
|
||||
(type: empty);
|
||||
throw new Error(`Unknown prop type for "${name}": "${type}"`);
|
||||
}
|
||||
}
|
||||
|
||||
function findProp(
|
||||
name: string,
|
||||
typeAnnotation: $FlowFixMe,
|
||||
optionalType: boolean,
|
||||
) {
|
||||
switch (typeAnnotation.type) {
|
||||
// Check for (T)
|
||||
case 'TSParenthesizedType':
|
||||
return findProp(name, typeAnnotation.typeAnnotation, optionalType);
|
||||
|
||||
// Check for optional type in union e.g. T | null | undefined
|
||||
case 'TSUnionType':
|
||||
return findProp(
|
||||
name,
|
||||
typeAnnotation.types.filter(
|
||||
t => t.type !== 'TSNullKeyword' && t.type !== 'TSUndefinedKeyword',
|
||||
)[0],
|
||||
optionalType ||
|
||||
typeAnnotation.types.some(
|
||||
t => t.type === 'TSNullKeyword' || t.type === 'TSUndefinedKeyword',
|
||||
),
|
||||
);
|
||||
|
||||
case 'TSTypeReference':
|
||||
// Check against optional type inside `WithDefault`
|
||||
if (typeAnnotation.typeName.name === 'WithDefault' && optionalType) {
|
||||
throw new Error(
|
||||
'WithDefault<> is optional and does not need to be marked as optional. Please remove the union of undefined and/or null',
|
||||
);
|
||||
}
|
||||
// Remove unwanted types
|
||||
if (
|
||||
typeAnnotation.typeName.name === 'DirectEventHandler' ||
|
||||
typeAnnotation.typeName.name === 'BubblingEventHandler'
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
if (
|
||||
name === 'style' &&
|
||||
typeAnnotation.type === 'GenericTypeAnnotation' &&
|
||||
typeAnnotation.typeName.name === 'ViewStyleProp'
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return {typeAnnotation, optionalType};
|
||||
default:
|
||||
return {typeAnnotation, optionalType};
|
||||
}
|
||||
}
|
||||
|
||||
type SchemaInfo = {
|
||||
name: string,
|
||||
optional: boolean,
|
||||
typeAnnotation: $FlowFixMe,
|
||||
defaultValue: $FlowFixMe,
|
||||
withNullDefault: boolean,
|
||||
};
|
||||
|
||||
function getSchemaInfo(
|
||||
property: PropAST,
|
||||
types: TypeDeclarationMap,
|
||||
): ?SchemaInfo {
|
||||
const name = property.key.name;
|
||||
|
||||
const value = getValueFromTypes(
|
||||
property.typeAnnotation.typeAnnotation,
|
||||
types,
|
||||
);
|
||||
|
||||
const foundProp = findProp(name, value, false);
|
||||
if (!foundProp) {
|
||||
return null;
|
||||
}
|
||||
let {typeAnnotation, optionalType} = foundProp;
|
||||
let optional = property.optional || optionalType;
|
||||
|
||||
// example: Readonly<{prop: string} | null | undefined>;
|
||||
if (
|
||||
value.type === 'TSTypeReference' &&
|
||||
typeAnnotation.typeParameters?.params[0].type === 'TSUnionType' &&
|
||||
typeAnnotation.typeParameters?.params[0].types.some(
|
||||
element =>
|
||||
element.type === 'TSNullKeyword' ||
|
||||
element.type === 'TSUndefinedKeyword',
|
||||
)
|
||||
) {
|
||||
optional = true;
|
||||
}
|
||||
|
||||
if (
|
||||
!property.optional &&
|
||||
value.type === 'TSTypeReference' &&
|
||||
typeAnnotation.typeName.name === 'WithDefault'
|
||||
) {
|
||||
throw new Error(
|
||||
`key ${name} must be optional if used with WithDefault<> annotation`,
|
||||
);
|
||||
}
|
||||
|
||||
let type = typeAnnotation.type;
|
||||
let defaultValue = null;
|
||||
let withNullDefault = false;
|
||||
if (
|
||||
type === 'TSTypeReference' &&
|
||||
typeAnnotation.typeName.name === 'WithDefault'
|
||||
) {
|
||||
if (typeAnnotation.typeParameters.params.length === 1) {
|
||||
throw new Error(
|
||||
`WithDefault requires two parameters, did you forget to provide a default value for "${name}"?`,
|
||||
);
|
||||
}
|
||||
|
||||
let defaultValueType = typeAnnotation.typeParameters.params[1].type;
|
||||
defaultValue = typeAnnotation.typeParameters.params[1].value;
|
||||
|
||||
if (defaultValueType === 'TSLiteralType') {
|
||||
defaultValueType = typeAnnotation.typeParameters.params[1].literal.type;
|
||||
defaultValue = typeAnnotation.typeParameters.params[1].literal.value;
|
||||
}
|
||||
|
||||
if (defaultValueType === 'TSNullKeyword') {
|
||||
defaultValue = null;
|
||||
withNullDefault = true;
|
||||
}
|
||||
|
||||
typeAnnotation = typeAnnotation.typeParameters.params[0];
|
||||
type =
|
||||
typeAnnotation.type === 'TSTypeReference'
|
||||
? typeAnnotation.typeName.name
|
||||
: typeAnnotation.type;
|
||||
}
|
||||
|
||||
return {
|
||||
name,
|
||||
optional,
|
||||
typeAnnotation,
|
||||
defaultValue,
|
||||
withNullDefault,
|
||||
};
|
||||
}
|
||||
|
||||
// $FlowFixMe[unclear-type] TODO(T108222691): Use flow-types for @babel/parser
|
||||
type PropAST = Object;
|
||||
|
||||
function verifyPropNotAlreadyDefined(
|
||||
props: $ReadOnlyArray<PropAST>,
|
||||
needleProp: PropAST,
|
||||
) {
|
||||
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 flattenProperties(
|
||||
typeDefinition: $ReadOnlyArray<PropAST>,
|
||||
types: TypeDeclarationMap,
|
||||
): $ReadOnlyArray<PropAST> {
|
||||
return typeDefinition
|
||||
.map(property => {
|
||||
if (property.type === 'TSPropertySignature') {
|
||||
return property;
|
||||
} else if (property.type === 'TSTypeReference') {
|
||||
return flattenProperties(
|
||||
getProperties(property.typeName.name, types),
|
||||
types,
|
||||
);
|
||||
} else if (property.type === 'TSExpressionWithTypeArguments') {
|
||||
return flattenProperties(
|
||||
getProperties(property.expression.name, types),
|
||||
types,
|
||||
);
|
||||
}
|
||||
})
|
||||
.filter(Boolean)
|
||||
.reduce((acc, item) => {
|
||||
if (Array.isArray(item)) {
|
||||
item.forEach(prop => {
|
||||
verifyPropNotAlreadyDefined(acc, prop);
|
||||
});
|
||||
return acc.concat(item);
|
||||
} else {
|
||||
verifyPropNotAlreadyDefined(acc, item);
|
||||
acc.push(item);
|
||||
return acc;
|
||||
}
|
||||
}, [])
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getProperties,
|
||||
getSchemaInfo,
|
||||
getTypeAnnotation,
|
||||
flattenProperties,
|
||||
};
|
||||
@@ -16,9 +16,11 @@ import type {ComponentSchemaBuilderConfig} from './schema.js';
|
||||
const {getTypes} = require('../utils');
|
||||
const {getCommands} = require('./commands');
|
||||
const {getEvents} = require('./events');
|
||||
const {getState} = require('./states');
|
||||
const {getExtendsProps, removeKnownExtends} = require('./extends');
|
||||
const {getCommandOptions, getOptions} = require('./options');
|
||||
const {getPropProperties, getProps} = require('./props');
|
||||
const {getProps} = require('./props');
|
||||
const {getProperties} = require('./componentsUtils.js');
|
||||
|
||||
/* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's
|
||||
* LTI update could not be added via codemod */
|
||||
@@ -111,8 +113,33 @@ function findComponentConfig(ast) {
|
||||
throw new Error('codegenNativeCommands may only be called once in a file');
|
||||
}
|
||||
|
||||
const unexportedStateTypes: Array<string> = ast.body
|
||||
.filter(
|
||||
node =>
|
||||
node.type === 'TSInterfaceDeclaration' &&
|
||||
node.id.name.indexOf('NativeState') >= 0,
|
||||
)
|
||||
.map(node => node.id.name);
|
||||
|
||||
const exportedStateTypes: Array<string> = namedExports
|
||||
.filter(
|
||||
node =>
|
||||
node.declaration.id &&
|
||||
node.declaration.id.name.indexOf('NativeState') >= 0,
|
||||
)
|
||||
.map(node => node.declaration.id.name);
|
||||
|
||||
const stateTypeName = exportedStateTypes.concat(unexportedStateTypes);
|
||||
|
||||
if (Array.isArray(stateTypeName) && stateTypeName.length > 1) {
|
||||
throw new Error(
|
||||
`Found ${stateTypeName.length} NativeStates for ${foundConfig.componentName}. Each component can have only 1 NativeState`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
...foundConfig,
|
||||
stateTypeName: stateTypeName.length === 1 ? stateTypeName[0] : '',
|
||||
commandTypeName:
|
||||
commandsTypeNames[0] == null
|
||||
? null
|
||||
@@ -186,6 +213,7 @@ function buildComponentSchema(ast): ComponentSchemaBuilderConfig {
|
||||
const {
|
||||
componentName,
|
||||
propsTypeName,
|
||||
stateTypeName,
|
||||
commandTypeName,
|
||||
commandOptionsExpression,
|
||||
optionsExpression,
|
||||
@@ -193,7 +221,7 @@ function buildComponentSchema(ast): ComponentSchemaBuilderConfig {
|
||||
|
||||
const types = getTypes(ast);
|
||||
|
||||
const propProperties = getPropProperties(propsTypeName, types);
|
||||
const propProperties = getProperties(propsTypeName, types);
|
||||
const commandOptions = getCommandOptions(commandOptionsExpression);
|
||||
|
||||
const commandProperties = getCommandProperties(
|
||||
@@ -210,7 +238,7 @@ function buildComponentSchema(ast): ComponentSchemaBuilderConfig {
|
||||
const events = getEvents(propProperties, types);
|
||||
const commands = getCommands(commandProperties, types);
|
||||
|
||||
return {
|
||||
const toRet = {
|
||||
filename: componentName,
|
||||
componentName,
|
||||
options,
|
||||
@@ -219,6 +247,17 @@ function buildComponentSchema(ast): ComponentSchemaBuilderConfig {
|
||||
props,
|
||||
commands,
|
||||
};
|
||||
|
||||
if (stateTypeName) {
|
||||
const stateProperties = getProperties(stateTypeName, types);
|
||||
const state = getState(stateProperties, types);
|
||||
return {
|
||||
...toRet,
|
||||
state,
|
||||
};
|
||||
}
|
||||
|
||||
return toRet;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
+12
-638
@@ -9,601 +9,27 @@
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
import type {ASTNode} from '../utils';
|
||||
|
||||
const {getValueFromTypes} = require('../utils.js');
|
||||
const {
|
||||
flattenProperties,
|
||||
getSchemaInfo,
|
||||
getTypeAnnotation,
|
||||
} = require('./componentsUtils.js');
|
||||
|
||||
import type {NamedShape, PropTypeAnnotation} from '../../../CodegenSchema.js';
|
||||
import type {TypeDeclarationMap} from '../utils.js';
|
||||
|
||||
function getPropProperties(
|
||||
propsTypeName: string,
|
||||
types: TypeDeclarationMap,
|
||||
): $FlowFixMe {
|
||||
const alias = types[propsTypeName];
|
||||
if (!alias) {
|
||||
throw new Error(
|
||||
`Failed to find definition for "${propsTypeName}", please check that you have a valid codegen typescript file`,
|
||||
);
|
||||
}
|
||||
const aliasKind =
|
||||
alias.type === 'TSInterfaceDeclaration' ? 'interface' : 'type';
|
||||
|
||||
try {
|
||||
if (aliasKind === 'interface') {
|
||||
return [...(alias.extends ?? []), ...alias.body.body];
|
||||
}
|
||||
|
||||
return (
|
||||
alias.typeAnnotation.members ||
|
||||
alias.typeAnnotation.typeParameters.params[0].members ||
|
||||
alias.typeAnnotation.typeParameters.params
|
||||
);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Failed to find ${aliasKind} definition for "${propsTypeName}", please check that you have a valid codegen typescript file`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function getTypeAnnotationForObjectAsArrayElement(
|
||||
objectType: $FlowFixMe,
|
||||
types: TypeDeclarationMap,
|
||||
) {
|
||||
return {
|
||||
type: 'ObjectTypeAnnotation',
|
||||
properties: flattenProperties(
|
||||
objectType.typeParameters.params[0].members ||
|
||||
objectType.typeParameters.params,
|
||||
types,
|
||||
)
|
||||
.map(prop => buildPropSchema(prop, types))
|
||||
.filter(Boolean),
|
||||
};
|
||||
}
|
||||
|
||||
function getTypeAnnotationForArrayOfArrayOfObject(
|
||||
typeAnnotation: $FlowFixMe,
|
||||
types: TypeDeclarationMap,
|
||||
) {
|
||||
// We need to go yet another level deeper to resolve
|
||||
// types that may be defined in a type alias
|
||||
const nestedObjectType = getValueFromTypes(typeAnnotation, types);
|
||||
|
||||
return {
|
||||
type: 'ArrayTypeAnnotation',
|
||||
elementType: getTypeAnnotationForObjectAsArrayElement(
|
||||
nestedObjectType,
|
||||
types,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
function getTypeAnnotationForArray(
|
||||
name: string,
|
||||
typeAnnotation: $FlowFixMe,
|
||||
defaultValue: $FlowFixMe | null,
|
||||
types: TypeDeclarationMap,
|
||||
) {
|
||||
if (typeAnnotation.type === 'TSParenthesizedType') {
|
||||
return getTypeAnnotationForArray(
|
||||
name,
|
||||
typeAnnotation.typeAnnotation,
|
||||
defaultValue,
|
||||
types,
|
||||
);
|
||||
}
|
||||
|
||||
const extractedTypeAnnotation = getValueFromTypes(typeAnnotation, types);
|
||||
|
||||
if (
|
||||
extractedTypeAnnotation.type === 'TSUnionType' &&
|
||||
extractedTypeAnnotation.types.some(
|
||||
t => t.type === 'TSNullKeyword' || t.type === 'TSUndefinedKeyword',
|
||||
)
|
||||
) {
|
||||
throw new Error(
|
||||
'Nested optionals such as "ReadonlyArray<boolean | null | undefined>" are not supported, please declare optionals at the top level of value definitions as in "ReadonlyArray<boolean> | null | undefined"',
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
extractedTypeAnnotation.type === 'TSTypeReference' &&
|
||||
extractedTypeAnnotation.typeName.name === 'WithDefault'
|
||||
) {
|
||||
throw new Error(
|
||||
'Nested defaults such as "ReadonlyArray<WithDefault<boolean, false>>" are not supported, please declare defaults at the top level of value definitions as in "WithDefault<ReadonlyArray<boolean>, false>"',
|
||||
);
|
||||
}
|
||||
|
||||
// Covers: T[]
|
||||
if (typeAnnotation.type === 'TSArrayType') {
|
||||
return getTypeAnnotationForArrayOfArrayOfObject(
|
||||
typeAnnotation.elementType,
|
||||
types,
|
||||
);
|
||||
}
|
||||
|
||||
if (extractedTypeAnnotation.type === 'TSTypeReference') {
|
||||
// Resolve the type alias if it's not defined inline
|
||||
const objectType = getValueFromTypes(extractedTypeAnnotation, types);
|
||||
|
||||
if (objectType.typeName.name === 'Readonly') {
|
||||
return getTypeAnnotationForObjectAsArrayElement(objectType, types);
|
||||
}
|
||||
|
||||
// Covers: ReadonlyArray<T>
|
||||
if (objectType.typeName.name === 'ReadonlyArray') {
|
||||
return getTypeAnnotationForArrayOfArrayOfObject(
|
||||
objectType.typeParameters.params[0],
|
||||
types,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const type =
|
||||
extractedTypeAnnotation.elementType === 'TSTypeReference'
|
||||
? extractedTypeAnnotation.elementType.typeName.name
|
||||
: extractedTypeAnnotation.elementType?.type ||
|
||||
extractedTypeAnnotation.typeName?.name ||
|
||||
extractedTypeAnnotation.type;
|
||||
|
||||
switch (type) {
|
||||
case 'TSNumberKeyword':
|
||||
return {
|
||||
type: 'FloatTypeAnnotation',
|
||||
};
|
||||
case 'ImageSource':
|
||||
return {
|
||||
type: 'ReservedPropTypeAnnotation',
|
||||
name: 'ImageSourcePrimitive',
|
||||
};
|
||||
case 'ColorValue':
|
||||
case 'ProcessedColorValue':
|
||||
return {
|
||||
type: 'ReservedPropTypeAnnotation',
|
||||
name: 'ColorPrimitive',
|
||||
};
|
||||
case 'PointValue':
|
||||
return {
|
||||
type: 'ReservedPropTypeAnnotation',
|
||||
name: 'PointPrimitive',
|
||||
};
|
||||
case 'EdgeInsetsValue':
|
||||
return {
|
||||
type: 'ReservedPropTypeAnnotation',
|
||||
name: 'EdgeInsetsPrimitive',
|
||||
};
|
||||
case 'Stringish':
|
||||
return {
|
||||
type: 'StringTypeAnnotation',
|
||||
};
|
||||
case 'Int32':
|
||||
return {
|
||||
type: 'Int32TypeAnnotation',
|
||||
};
|
||||
case 'Double':
|
||||
return {
|
||||
type: 'DoubleTypeAnnotation',
|
||||
};
|
||||
case 'Float':
|
||||
return {
|
||||
type: 'FloatTypeAnnotation',
|
||||
};
|
||||
case 'TSBooleanKeyword':
|
||||
return {
|
||||
type: 'BooleanTypeAnnotation',
|
||||
};
|
||||
case 'TSStringKeyword':
|
||||
return {
|
||||
type: 'StringTypeAnnotation',
|
||||
};
|
||||
case 'TSUnionType':
|
||||
typeAnnotation.types.reduce((lastType, currType) => {
|
||||
const lastFlattenedType =
|
||||
lastType && lastType.type === 'TSLiteralType'
|
||||
? lastType.literal.type
|
||||
: lastType.type;
|
||||
const currFlattenedType =
|
||||
currType.type === 'TSLiteralType'
|
||||
? currType.literal.type
|
||||
: currType.type;
|
||||
|
||||
if (lastFlattenedType && currFlattenedType !== lastFlattenedType) {
|
||||
throw new Error(`Mixed types are not supported (see "${name}")`);
|
||||
}
|
||||
return currType;
|
||||
});
|
||||
|
||||
if (defaultValue === null) {
|
||||
throw new Error(`A default enum value is required for "${name}"`);
|
||||
}
|
||||
|
||||
const unionType = typeAnnotation.types[0].type;
|
||||
if (
|
||||
unionType === 'TSLiteralType' &&
|
||||
typeAnnotation.types[0].literal?.type === 'StringLiteral'
|
||||
) {
|
||||
return {
|
||||
type: 'StringEnumTypeAnnotation',
|
||||
default: (defaultValue: string),
|
||||
options: typeAnnotation.types.map(option => option.literal.value),
|
||||
};
|
||||
} else if (
|
||||
unionType === 'TSLiteralType' &&
|
||||
typeAnnotation.types[0].literal?.type === 'NumericLiteral'
|
||||
) {
|
||||
throw new Error(
|
||||
`Arrays of int enums are not supported (see: "${name}")`,
|
||||
);
|
||||
} else {
|
||||
throw new Error(
|
||||
`Unsupported union type for "${name}", received "${
|
||||
unionType === 'TSLiteralType'
|
||||
? typeAnnotation.types[0].literal?.type
|
||||
: unionType
|
||||
}"`,
|
||||
);
|
||||
}
|
||||
default:
|
||||
(type: empty);
|
||||
throw new Error(`Unknown prop type for "${name}": ${type}`);
|
||||
}
|
||||
}
|
||||
|
||||
function getTypeAnnotation(
|
||||
name: string,
|
||||
annotation: $FlowFixMe | ASTNode,
|
||||
defaultValue: $FlowFixMe | null,
|
||||
withNullDefault: boolean,
|
||||
types: TypeDeclarationMap,
|
||||
) {
|
||||
const typeAnnotation = getValueFromTypes(annotation, types);
|
||||
|
||||
// Covers: (T)
|
||||
if (typeAnnotation.type === 'TSParenthesizedType') {
|
||||
return getTypeAnnotation(
|
||||
name,
|
||||
typeAnnotation.typeAnnotation,
|
||||
defaultValue,
|
||||
withNullDefault,
|
||||
types,
|
||||
);
|
||||
}
|
||||
|
||||
// Covers: readonly T[]
|
||||
if (
|
||||
typeAnnotation.type === 'TSTypeOperator' &&
|
||||
typeAnnotation.operator === 'readonly' &&
|
||||
typeAnnotation.typeAnnotation.type === 'TSArrayType'
|
||||
) {
|
||||
return {
|
||||
type: 'ArrayTypeAnnotation',
|
||||
elementType: getTypeAnnotationForArray(
|
||||
name,
|
||||
typeAnnotation.typeAnnotation.elementType,
|
||||
defaultValue,
|
||||
types,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
// Covers: ReadonlyArray<T>
|
||||
if (
|
||||
typeAnnotation.type === 'TSTypeReference' &&
|
||||
typeAnnotation.typeName.name === 'ReadonlyArray'
|
||||
) {
|
||||
return {
|
||||
type: 'ArrayTypeAnnotation',
|
||||
elementType: getTypeAnnotationForArray(
|
||||
name,
|
||||
typeAnnotation.typeParameters.params[0],
|
||||
defaultValue,
|
||||
types,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
// Covers: Readonly<T[]>
|
||||
if (
|
||||
typeAnnotation.type === 'TSTypeReference' &&
|
||||
typeAnnotation.typeName?.name === 'Readonly' &&
|
||||
typeAnnotation.typeParameters.type === 'TSTypeParameterInstantiation' &&
|
||||
typeAnnotation.typeParameters.params[0].type === 'TSArrayType'
|
||||
) {
|
||||
return {
|
||||
type: 'ArrayTypeAnnotation',
|
||||
elementType: getTypeAnnotationForArray(
|
||||
name,
|
||||
typeAnnotation.typeParameters.params[0],
|
||||
defaultValue,
|
||||
types,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
(typeAnnotation.type === 'TSTypeReference' ||
|
||||
typeAnnotation.type === 'TSTypeLiteral') &&
|
||||
typeAnnotation.typeName?.name === 'Readonly'
|
||||
) {
|
||||
const rawProperties =
|
||||
typeAnnotation.typeParameters.params[0].members ||
|
||||
(typeAnnotation.typeParameters.params[0].types &&
|
||||
typeAnnotation.typeParameters.params[0].types[0].members) ||
|
||||
typeAnnotation.typeParameters.params;
|
||||
|
||||
const flattenedProperties = flattenProperties(rawProperties, types);
|
||||
|
||||
const properties = flattenedProperties
|
||||
.map(prop => buildPropSchema(prop, types))
|
||||
.filter(Boolean);
|
||||
|
||||
return {
|
||||
type: 'ObjectTypeAnnotation',
|
||||
properties,
|
||||
};
|
||||
}
|
||||
|
||||
const type =
|
||||
typeAnnotation.type === 'TSTypeReference' ||
|
||||
typeAnnotation.type === 'TSTypeAliasDeclaration'
|
||||
? typeAnnotation.typeName.name
|
||||
: typeAnnotation.type;
|
||||
|
||||
switch (type) {
|
||||
case 'ImageSource':
|
||||
return {
|
||||
type: 'ReservedPropTypeAnnotation',
|
||||
name: 'ImageSourcePrimitive',
|
||||
};
|
||||
case 'ColorValue':
|
||||
case 'ProcessedColorValue':
|
||||
return {
|
||||
type: 'ReservedPropTypeAnnotation',
|
||||
name: 'ColorPrimitive',
|
||||
};
|
||||
case 'ColorArrayValue':
|
||||
return {
|
||||
type: 'ArrayTypeAnnotation',
|
||||
elementType: {
|
||||
type: 'ReservedPropTypeAnnotation',
|
||||
name: 'ColorPrimitive',
|
||||
},
|
||||
};
|
||||
case 'PointValue':
|
||||
return {
|
||||
type: 'ReservedPropTypeAnnotation',
|
||||
name: 'PointPrimitive',
|
||||
};
|
||||
case 'EdgeInsetsValue':
|
||||
return {
|
||||
type: 'ReservedPropTypeAnnotation',
|
||||
name: 'EdgeInsetsPrimitive',
|
||||
};
|
||||
case 'Int32':
|
||||
return {
|
||||
type: 'Int32TypeAnnotation',
|
||||
default: ((defaultValue ? defaultValue : 0): number),
|
||||
};
|
||||
case 'Double':
|
||||
return {
|
||||
type: 'DoubleTypeAnnotation',
|
||||
default: ((defaultValue ? defaultValue : 0): number),
|
||||
};
|
||||
case 'Float':
|
||||
return {
|
||||
type: 'FloatTypeAnnotation',
|
||||
default: withNullDefault
|
||||
? (defaultValue: number | null)
|
||||
: ((defaultValue ? defaultValue : 0): number),
|
||||
};
|
||||
case 'TSBooleanKeyword':
|
||||
return {
|
||||
type: 'BooleanTypeAnnotation',
|
||||
default: withNullDefault
|
||||
? (defaultValue: boolean | null)
|
||||
: ((defaultValue == null ? false : defaultValue): boolean),
|
||||
};
|
||||
case 'TSStringKeyword':
|
||||
if (typeof defaultValue !== 'undefined') {
|
||||
return {
|
||||
type: 'StringTypeAnnotation',
|
||||
default: (defaultValue: string | null),
|
||||
};
|
||||
}
|
||||
throw new Error(`A default string (or null) is required for "${name}"`);
|
||||
case 'Stringish':
|
||||
if (typeof defaultValue !== 'undefined') {
|
||||
return {
|
||||
type: 'StringTypeAnnotation',
|
||||
default: (defaultValue: string | null),
|
||||
};
|
||||
}
|
||||
throw new Error(`A default string (or null) is required for "${name}"`);
|
||||
case 'TSUnionType':
|
||||
typeAnnotation.types.reduce((lastType, currType) => {
|
||||
const lastFlattenedType =
|
||||
lastType && lastType.type === 'TSLiteralType'
|
||||
? lastType.literal.type
|
||||
: lastType.type;
|
||||
const currFlattenedType =
|
||||
currType.type === 'TSLiteralType'
|
||||
? currType.literal.type
|
||||
: currType.type;
|
||||
|
||||
if (lastFlattenedType && currFlattenedType !== lastFlattenedType) {
|
||||
throw new Error(`Mixed types are not supported (see "${name}")`);
|
||||
}
|
||||
return currType;
|
||||
});
|
||||
|
||||
if (defaultValue === null) {
|
||||
throw new Error(`A default enum value is required for "${name}"`);
|
||||
}
|
||||
|
||||
const unionType = typeAnnotation.types[0].type;
|
||||
if (
|
||||
unionType === 'TSLiteralType' &&
|
||||
typeAnnotation.types[0].literal?.type === 'StringLiteral'
|
||||
) {
|
||||
return {
|
||||
type: 'StringEnumTypeAnnotation',
|
||||
default: (defaultValue: string),
|
||||
options: typeAnnotation.types.map(option => option.literal.value),
|
||||
};
|
||||
} else if (
|
||||
unionType === 'TSLiteralType' &&
|
||||
typeAnnotation.types[0].literal?.type === 'NumericLiteral'
|
||||
) {
|
||||
return {
|
||||
type: 'Int32EnumTypeAnnotation',
|
||||
default: (defaultValue: number),
|
||||
options: typeAnnotation.types.map(option => option.literal.value),
|
||||
};
|
||||
} else {
|
||||
throw new Error(
|
||||
`Unsupported union type for "${name}", received "${
|
||||
unionType === 'TSLiteralType'
|
||||
? typeAnnotation.types[0].literal?.type
|
||||
: unionType
|
||||
}"`,
|
||||
);
|
||||
}
|
||||
case 'TSNumberKeyword':
|
||||
throw new Error(
|
||||
`Cannot use "${type}" type annotation for "${name}": must use a specific numeric type like Int32, Double, or Float`,
|
||||
);
|
||||
default:
|
||||
(type: empty);
|
||||
throw new Error(`Unknown prop type for "${name}": "${type}"`);
|
||||
}
|
||||
}
|
||||
|
||||
function findProp(
|
||||
name: string,
|
||||
typeAnnotation: $FlowFixMe,
|
||||
optionalType: boolean,
|
||||
) {
|
||||
switch (typeAnnotation.type) {
|
||||
// Check for (T)
|
||||
case 'TSParenthesizedType':
|
||||
return findProp(name, typeAnnotation.typeAnnotation, optionalType);
|
||||
|
||||
// Check for optional type in union e.g. T | null | undefined
|
||||
case 'TSUnionType':
|
||||
return findProp(
|
||||
name,
|
||||
typeAnnotation.types.filter(
|
||||
t => t.type !== 'TSNullKeyword' && t.type !== 'TSUndefinedKeyword',
|
||||
)[0],
|
||||
optionalType ||
|
||||
typeAnnotation.types.some(
|
||||
t => t.type === 'TSNullKeyword' || t.type === 'TSUndefinedKeyword',
|
||||
),
|
||||
);
|
||||
|
||||
case 'TSTypeReference':
|
||||
// Check against optional type inside `WithDefault`
|
||||
if (typeAnnotation.typeName.name === 'WithDefault' && optionalType) {
|
||||
throw new Error(
|
||||
'WithDefault<> is optional and does not need to be marked as optional. Please remove the union of undefined and/or null',
|
||||
);
|
||||
}
|
||||
// Remove unwanted types
|
||||
if (
|
||||
typeAnnotation.typeName.name === 'DirectEventHandler' ||
|
||||
typeAnnotation.typeName.name === 'BubblingEventHandler'
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
if (
|
||||
name === 'style' &&
|
||||
typeAnnotation.type === 'GenericTypeAnnotation' &&
|
||||
typeAnnotation.typeName.name === 'ViewStyleProp'
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return {typeAnnotation, optionalType};
|
||||
default:
|
||||
return {typeAnnotation, optionalType};
|
||||
}
|
||||
}
|
||||
// $FlowFixMe[unclear-type] there's no flowtype for ASTs
|
||||
type PropAST = Object;
|
||||
|
||||
function buildPropSchema(
|
||||
property: PropAST,
|
||||
types: TypeDeclarationMap,
|
||||
): ?NamedShape<PropTypeAnnotation> {
|
||||
const name = property.key.name;
|
||||
|
||||
const value = getValueFromTypes(
|
||||
property.typeAnnotation.typeAnnotation,
|
||||
types,
|
||||
);
|
||||
|
||||
const foundProp = findProp(name, value, false);
|
||||
if (!foundProp) {
|
||||
const info = getSchemaInfo(property, types);
|
||||
if (info == null) {
|
||||
return null;
|
||||
}
|
||||
let {typeAnnotation, optionalType} = foundProp;
|
||||
let optional = property.optional || optionalType;
|
||||
|
||||
// example: Readonly<{prop: string} | null | undefined>;
|
||||
if (
|
||||
value.type === 'TSTypeReference' &&
|
||||
typeAnnotation.typeParameters?.params[0].type === 'TSUnionType' &&
|
||||
typeAnnotation.typeParameters?.params[0].types.some(
|
||||
element =>
|
||||
element.type === 'TSNullKeyword' ||
|
||||
element.type === 'TSUndefinedKeyword',
|
||||
)
|
||||
) {
|
||||
optional = true;
|
||||
}
|
||||
|
||||
if (
|
||||
!property.optional &&
|
||||
value.type === 'TSTypeReference' &&
|
||||
typeAnnotation.typeName.name === 'WithDefault'
|
||||
) {
|
||||
throw new Error(
|
||||
`key ${name} must be optional if used with WithDefault<> annotation`,
|
||||
);
|
||||
}
|
||||
|
||||
let type = typeAnnotation.type;
|
||||
let defaultValue = null;
|
||||
let withNullDefault = false;
|
||||
if (
|
||||
type === 'TSTypeReference' &&
|
||||
typeAnnotation.typeName.name === 'WithDefault'
|
||||
) {
|
||||
if (typeAnnotation.typeParameters.params.length === 1) {
|
||||
throw new Error(
|
||||
`WithDefault requires two parameters, did you forget to provide a default value for "${name}"?`,
|
||||
);
|
||||
}
|
||||
|
||||
let defaultValueType = typeAnnotation.typeParameters.params[1].type;
|
||||
defaultValue = typeAnnotation.typeParameters.params[1].value;
|
||||
|
||||
if (defaultValueType === 'TSLiteralType') {
|
||||
defaultValueType = typeAnnotation.typeParameters.params[1].literal.type;
|
||||
defaultValue = typeAnnotation.typeParameters.params[1].literal.value;
|
||||
}
|
||||
|
||||
if (defaultValueType === 'TSNullKeyword') {
|
||||
defaultValue = null;
|
||||
withNullDefault = true;
|
||||
}
|
||||
|
||||
typeAnnotation = typeAnnotation.typeParameters.params[0];
|
||||
type =
|
||||
typeAnnotation.type === 'TSTypeReference'
|
||||
? typeAnnotation.typeName.name
|
||||
: typeAnnotation.type;
|
||||
}
|
||||
|
||||
const {name, optional, typeAnnotation, defaultValue, withNullDefault} = info;
|
||||
return {
|
||||
name,
|
||||
optional,
|
||||
@@ -613,72 +39,20 @@ function buildPropSchema(
|
||||
defaultValue,
|
||||
withNullDefault,
|
||||
types,
|
||||
buildPropSchema,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
// $FlowFixMe[unclear-type] TODO(T108222691): Use flow-types for @babel/parser
|
||||
type PropAST = Object;
|
||||
|
||||
function verifyPropNotAlreadyDefined(
|
||||
props: $ReadOnlyArray<PropAST>,
|
||||
needleProp: PropAST,
|
||||
) {
|
||||
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 flattenProperties(
|
||||
typeDefinition: $ReadOnlyArray<PropAST>,
|
||||
types: TypeDeclarationMap,
|
||||
) {
|
||||
return typeDefinition
|
||||
.map(property => {
|
||||
if (property.type === 'TSPropertySignature') {
|
||||
return property;
|
||||
} else if (property.type === 'TSTypeReference') {
|
||||
return flattenProperties(
|
||||
getPropProperties(property.typeName.name, types),
|
||||
types,
|
||||
);
|
||||
} else if (property.type === 'TSExpressionWithTypeArguments') {
|
||||
return flattenProperties(
|
||||
getPropProperties(property.expression.name, types),
|
||||
types,
|
||||
);
|
||||
}
|
||||
})
|
||||
.filter(Boolean)
|
||||
.reduce((acc, item) => {
|
||||
if (Array.isArray(item)) {
|
||||
item.forEach(prop => {
|
||||
verifyPropNotAlreadyDefined(acc, prop);
|
||||
});
|
||||
return acc.concat(item);
|
||||
} else {
|
||||
verifyPropNotAlreadyDefined(acc, item);
|
||||
acc.push(item);
|
||||
return acc;
|
||||
}
|
||||
}, [])
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function getProps(
|
||||
typeDefinition: $ReadOnlyArray<PropAST>,
|
||||
types: TypeDeclarationMap,
|
||||
): $ReadOnlyArray<NamedShape<PropTypeAnnotation>> {
|
||||
return flattenProperties(typeDefinition, types)
|
||||
.map(property => {
|
||||
return buildPropSchema(property, types);
|
||||
})
|
||||
.map(property => buildPropSchema(property, types))
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getProps,
|
||||
getPropProperties,
|
||||
};
|
||||
|
||||
@@ -15,6 +15,7 @@ import type {
|
||||
NamedShape,
|
||||
CommandTypeAnnotation,
|
||||
PropTypeAnnotation,
|
||||
StateTypeAnnotation,
|
||||
ExtendsPropsShape,
|
||||
SchemaType,
|
||||
OptionsShape,
|
||||
@@ -27,6 +28,7 @@ export type ComponentSchemaBuilderConfig = $ReadOnly<{
|
||||
events: $ReadOnlyArray<EventTypeShape>,
|
||||
props: $ReadOnlyArray<NamedShape<PropTypeAnnotation>>,
|
||||
commands: $ReadOnlyArray<NamedShape<CommandTypeAnnotation>>,
|
||||
state?: $ReadOnlyArray<NamedShape<StateTypeAnnotation>>,
|
||||
options?: ?OptionsShape,
|
||||
}>;
|
||||
|
||||
@@ -36,6 +38,7 @@ function wrapComponentSchema({
|
||||
extendsProps,
|
||||
events,
|
||||
props,
|
||||
state,
|
||||
options,
|
||||
commands,
|
||||
}: ComponentSchemaBuilderConfig): SchemaType {
|
||||
@@ -50,6 +53,7 @@ function wrapComponentSchema({
|
||||
events,
|
||||
props,
|
||||
commands,
|
||||
state,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* 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-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
const {
|
||||
flattenProperties,
|
||||
getSchemaInfo,
|
||||
getTypeAnnotation,
|
||||
} = require('./componentsUtils.js');
|
||||
|
||||
import type {StateTypeAnnotation, NamedShape} from '../../../CodegenSchema.js';
|
||||
import type {TypeDeclarationMap} from '../utils.js';
|
||||
|
||||
// $FlowFixMe[unclear-type] there's no flowtype for ASTs
|
||||
type PropAST = Object;
|
||||
|
||||
function buildStateSchema(
|
||||
property: PropAST,
|
||||
types: TypeDeclarationMap,
|
||||
): ?NamedShape<StateTypeAnnotation> {
|
||||
const info = getSchemaInfo(property, types);
|
||||
if (info == null) {
|
||||
return null;
|
||||
}
|
||||
const {name, optional, typeAnnotation, defaultValue, withNullDefault} = info;
|
||||
return {
|
||||
name,
|
||||
optional,
|
||||
typeAnnotation: getTypeAnnotation(
|
||||
name,
|
||||
typeAnnotation,
|
||||
defaultValue,
|
||||
withNullDefault,
|
||||
types,
|
||||
buildStateSchema,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
function getState(
|
||||
typeDefinition: $ReadOnlyArray<PropAST>,
|
||||
types: TypeDeclarationMap,
|
||||
): $ReadOnlyArray<NamedShape<StateTypeAnnotation>> {
|
||||
return flattenProperties(typeDefinition, types)
|
||||
.map(property => buildStateSchema(property, types))
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getState,
|
||||
};
|
||||
Reference in New Issue
Block a user