feat(smart-app): implement complete mobile app MVP

- App.tsx: full navigation (Auth stack + Main tabs with 5 screens)
- Auth: LoginScreen, RegisterScreen, ForgotPasswordScreen
- HomeScreen: dashboard with IoT metrics, weather widget, alerts, quick actions, sensors
- MapScreen: interactive map with layer toggles (6 layers)
- MarketplaceScreen: categories (6), products (5), search
- ChatScreen: AI chat with quick prompts (4), bot responses
- ProfileScreen: user info, stats, menu (9 items), logout
- AlertsScreen: alert list with severity, acknowledge
- SensorsScreen: sensor list with type filters (6 types), search
- ZonesScreen: zone cards with stats
- SettingsScreen: language picker (FR/EN/ES/DE), privacy, about
- Stores: iotStore (sensors, zones, alerts), notificationStore, uiStore + i18n
- Hooks: useSensors, useAlerts, useNotifications, useLocation
- Components: Card, Button, LoadingSpinner, ErrorBoundary, Header
- Services: iotService, notificationService (with axios API client)
- Utils: formatters (temp, AQI, noise, dates), validators (email, password, IBAN)
- Theme: colors.ts with full design system (Blue Ocean palette)
- Ditto: fixed MongoDB connection, new JWT secrets, official gateway image
This commit is contained in:
Eric FELIXINE
2026-06-01 18:00:35 -04:00
parent 08ca495bde
commit e30ae8ed09
35578 changed files with 3703534 additions and 43 deletions

View File

@@ -0,0 +1,583 @@
/**
* 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
*/
// @licenselint-loose-mode
'use strict';
const COMMANDS_DEFINED_INLINE = `
/**
* 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
* @flow strict-local
*/
'use strict';
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
export type ModuleProps = $ReadOnly<{|
...ViewProps,
// No props
|}>;
export const Commands = codegenNativeCommands<{
+hotspotUpdate: (ref: React.Ref<'RCTView'>, x: Int32, y: Int32) => void,
}>({
supportedCommands: ['hotspotUpdate'],
});
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const COMMANDS_DEFINED_MULTIPLE_TIMES = `
/**
* 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
* @flow strict-local
*/
'use strict';
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
+hotspotUpdate: (viewRef: React.Ref<'RCTView'>, x: Int32, y: Int32) => void;
}
export type ModuleProps = $ReadOnly<{|
...ViewProps,
// No props or events
|}>;
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate'],
});
export const Commands2 = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate'],
});
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const COMMANDS_DEFINED_WITHOUT_REF = `
/**
* 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
* @flow strict-local
*/
'use strict';
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
+hotspotUpdate: (x: Int32, y: Int32) => void;
}
export type ModuleProps = $ReadOnly<{|
...ViewProps,
// No props or events
|}>;
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate'],
});
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const COMMANDS_DEFINED_WITH_NULLABLE_REF = `
/**
* 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
* @flow strict-local
*/
'use strict';
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
+hotspotUpdate: (viewRef: ?React.Ref<'RCTView'>, x: Int32, y: Int32) => void;
}
export type ModuleProps = $ReadOnly<{|
...ViewProps,
// No props or events
|}>;
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate'],
});
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const COMMANDS_DEFINED_WITH_MISMATCHED_METHOD_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
* @flow strict-local
*/
'use strict';
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
+hotspotUpdate: (viewRef: React.Ref<'RCTView'>, x: Int32, y: Int32) => void;
+scrollTo: (
viewRef: React.Ref<'RCTView'>,
y: Int32,
animated: boolean,
) => void;
}
export type ModuleProps = $ReadOnly<{|
...ViewProps,
// No props or events
|}>;
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['scrollTo'],
});
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const COMMANDS_DEFINED_WITHOUT_METHOD_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
* @flow strict-local
*/
'use strict';
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
+hotspotUpdate: (viewRef: React.Ref<'RCTView'>, x: Int32, y: Int32) => void;
+scrollTo: (
viewRef: React.Ref<'RCTView'>,
y: Int32,
animated: boolean,
) => void;
}
export type ModuleProps = $ReadOnly<{|
...ViewProps,
// No props or events
|}>;
export const Commands = codegenNativeCommands<NativeCommands>();
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const NULLABLE_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
* @flow strict-local
*/
'use strict';
const codegenNativeComponent = require('codegenNativeComponent');
import type {WithDefault, Float} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
export type ModuleProps = $ReadOnly<{|
...ViewProps,
nullable_with_default: ?WithDefault<Float, 1.0>,
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const NON_OPTIONAL_KEY_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
* @flow strict-local
*/
'use strict';
const codegenNativeComponent = require('codegenNativeComponent');
import type {WithDefault, Float} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
export type ModuleProps = $ReadOnly<{|
...ViewProps,
required_key_with_default: WithDefault<Float, 1.0>,
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROPS_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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export type ModuleProps = $ReadOnly<{|
...ViewProps,
isEnabled: string,
isEnabled: boolean,
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROPS_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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
type PropsInFile = $ReadOnly<{|
isEnabled: boolean,
|}>;
export type ModuleProps = $ReadOnly<{|
...ViewProps,
...PropsInFile,
isEnabled: boolean,
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROPS_SPREAD_CONFLICTS_WITH_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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
type PropsInFile = $ReadOnly<{|
isEnabled: boolean,
|}>;
export type ModuleProps = $ReadOnly<{|
...ViewProps,
isEnabled: boolean,
...PropsInFile,
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROP_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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export type ModuleProps = $ReadOnly<{|
...ViewProps,
someProp: number
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROP_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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export type ModuleProps = $ReadOnly<{|
...ViewProps,
someProp?: WithDefault<'foo' | 1, 1>
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROP_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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export type ModuleProps = $ReadOnly<{|
...ViewProps,
someProp?: WithDefault<false | true, false>
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROP_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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export type ModuleProps = $ReadOnly<{|
...ViewProps,
someProp?: WithDefault<$ReadOnlyArray<'foo' | 1>, 1>
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROP_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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export type ModuleProps = $ReadOnly<{|
...ViewProps,
someProp?: WithDefault<$ReadOnlyArray<false | true>, false>
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROP_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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export type ModuleProps = $ReadOnly<{|
...ViewProps,
someProp?: WithDefault<$ReadOnlyArray<0 | 1>, 0>
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
module.exports = {
COMMANDS_DEFINED_INLINE,
COMMANDS_DEFINED_MULTIPLE_TIMES,
COMMANDS_DEFINED_WITH_MISMATCHED_METHOD_NAMES,
COMMANDS_DEFINED_WITHOUT_METHOD_NAMES,
COMMANDS_DEFINED_WITHOUT_REF,
COMMANDS_DEFINED_WITH_NULLABLE_REF,
NULLABLE_WITH_DEFAULT,
NON_OPTIONAL_KEY_WITH_DEFAULT_VALUE,
PROPS_CONFLICT_NAMES,
PROPS_CONFLICT_WITH_SPREAD_PROPS,
PROPS_SPREAD_CONFLICTS_WITH_PROPS,
PROP_NUMBER_TYPE,
PROP_MIXED_ENUM,
PROP_ENUM_BOOLEAN,
PROP_ARRAY_MIXED_ENUM,
PROP_ARRAY_ENUM_BOOLEAN,
PROP_ARRAY_ENUM_INT,
};

View File

@@ -0,0 +1,600 @@
/**
* 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
*/
// @licenselint-loose-mode
'use strict';
const COMMANDS_DEFINED_INLINE = `
/**
* 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
* @flow strict-local
*/
'use strict';
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
export type ModuleProps = $ReadOnly<{|
...ViewProps,
// No props
|}>;
export const Commands = codegenNativeCommands<{
+hotspotUpdate: (ref: React.Ref<'RCTView'>, x: Int32, y: Int32) => void,
}>({
supportedCommands: ['hotspotUpdate'],
});
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const COMMANDS_DEFINED_MULTIPLE_TIMES = `
/**
* 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
* @flow strict-local
*/
'use strict';
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
+hotspotUpdate: (viewRef: React.Ref<'RCTView'>, x: Int32, y: Int32) => void;
}
export type ModuleProps = $ReadOnly<{|
...ViewProps,
// No props or events
|}>;
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate'],
});
export const Commands2 = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate'],
});
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const COMMANDS_DEFINED_WITHOUT_REF = `
/**
* 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
* @flow strict-local
*/
'use strict';
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
+hotspotUpdate: (x: Int32, y: Int32) => void;
}
export type ModuleProps = $ReadOnly<{|
...ViewProps,
// No props or events
|}>;
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate'],
});
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const COMMANDS_DEFINED_WITH_NULLABLE_REF = `
/**
* 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
* @flow strict-local
*/
'use strict';
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
+hotspotUpdate: (viewRef: ?React.Ref<'RCTView'>, x: Int32, y: Int32) => void;
}
export type ModuleProps = $ReadOnly<{|
...ViewProps,
// No props or events
|}>;
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['hotspotUpdate'],
});
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const COMMANDS_DEFINED_WITH_MISMATCHED_METHOD_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
* @flow strict-local
*/
'use strict';
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
+hotspotUpdate: (viewRef: React.Ref<'RCTView'>, x: Int32, y: Int32) => void;
+scrollTo: (
viewRef: React.Ref<'RCTView'>,
y: Int32,
animated: boolean,
) => void;
}
export type ModuleProps = $ReadOnly<{|
...ViewProps,
// No props or events
|}>;
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['scrollTo'],
});
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const COMMANDS_DEFINED_WITHOUT_METHOD_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
* @flow strict-local
*/
'use strict';
const codegenNativeCommands = require('codegenNativeCommands');
const codegenNativeComponent = require('codegenNativeComponent');
import type {Int32} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
interface NativeCommands {
+hotspotUpdate: (viewRef: React.Ref<'RCTView'>, x: Int32, y: Int32) => void;
+scrollTo: (
viewRef: React.Ref<'RCTView'>,
y: Int32,
animated: boolean,
) => void;
}
export type ModuleProps = $ReadOnly<{|
...ViewProps,
// No props or events
|}>;
export const Commands = codegenNativeCommands<NativeCommands>();
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const NULLABLE_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
* @flow strict-local
*/
'use strict';
const codegenNativeComponent = require('codegenNativeComponent');
import type {WithDefault, Float} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
export type ModuleProps = $ReadOnly<{|
...ViewProps,
nullable_with_default: ?WithDefault<Float, 1.0>,
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const NON_OPTIONAL_KEY_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
* @flow strict-local
*/
'use strict';
const codegenNativeComponent = require('codegenNativeComponent');
import type {WithDefault, Float} from 'CodegenTypes';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
export type ModuleProps = $ReadOnly<{|
...ViewProps,
required_key_with_default: WithDefault<Float, 1.0>,
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROPS_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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export type ModuleProps = $ReadOnly<{|
...ViewProps,
isEnabled: string,
isEnabled: boolean,
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROPS_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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
type PropsInFile = $ReadOnly<{|
isEnabled: boolean,
|}>;
export type ModuleProps = $ReadOnly<{|
...ViewProps,
...PropsInFile,
isEnabled: boolean,
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROPS_SPREAD_CONFLICTS_WITH_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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
type PropsInFile = $ReadOnly<{|
isEnabled: boolean,
|}>;
export type ModuleProps = $ReadOnly<{|
...ViewProps,
isEnabled: boolean,
...PropsInFile,
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROP_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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export type ModuleProps = $ReadOnly<{|
...ViewProps,
someProp: number
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROP_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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export type ModuleProps = $ReadOnly<{|
...ViewProps,
someProp?: WithDefault<'foo' | 1, 1>
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROP_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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export type ModuleProps = $ReadOnly<{|
...ViewProps,
someProp?: WithDefault<false | true, false>
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROP_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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export type ModuleProps = $ReadOnly<{|
...ViewProps,
someProp?: WithDefault<$ReadOnlyArray<'foo' | 1>, 1>
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROP_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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export type ModuleProps = $ReadOnly<{|
...ViewProps,
someProp?: WithDefault<$ReadOnlyArray<false | true>, false>
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
const PROP_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
* @flow strict-local
*/
'use strict';
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export type ModuleProps = $ReadOnly<{|
...ViewProps,
someProp?: WithDefault<$ReadOnlyArray<0 | 1>, 0>
|}>;
export default (codegenNativeComponent<ModuleProps>(
'Module',
): HostComponent<ModuleProps>);
`;
module.exports = {
COMMANDS_DEFINED_INLINE,
COMMANDS_DEFINED_MULTIPLE_TIMES,
COMMANDS_DEFINED_WITH_MISMATCHED_METHOD_NAMES,
COMMANDS_DEFINED_WITHOUT_METHOD_NAMES,
COMMANDS_DEFINED_WITHOUT_REF,
COMMANDS_DEFINED_WITH_NULLABLE_REF,
NULLABLE_WITH_DEFAULT,
NON_OPTIONAL_KEY_WITH_DEFAULT_VALUE,
PROPS_CONFLICT_NAMES,
PROPS_CONFLICT_WITH_SPREAD_PROPS,
PROPS_SPREAD_CONFLICTS_WITH_PROPS,
PROP_NUMBER_TYPE,
PROP_MIXED_ENUM,
PROP_ENUM_BOOLEAN,
PROP_ARRAY_MIXED_ENUM,
PROP_ARRAY_ENUM_BOOLEAN,
PROP_ARRAY_ENUM_INT,
};

View File

@@ -0,0 +1,131 @@
/**
* 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 _require = require('../utils.js'),
getValueFromTypes = _require.getValueFromTypes;
// $FlowFixMe[unclear-type] there's no flowtype for ASTs
function buildCommandSchema(property, types) {
const name = property.key.name;
const optional = property.optional;
const value = getValueFromTypes(property.value, types);
const firstParam = value.params[0].typeAnnotation;
if (
!(
firstParam.id != null &&
firstParam.id.type === 'QualifiedTypeIdentifier' &&
firstParam.id.qualification.name === 'React' &&
firstParam.id.id.name === 'ElementRef'
)
) {
throw new Error(
`The first argument of method ${name} must be of type React.ElementRef<>`,
);
}
const params = value.params.slice(1).map(param => {
const paramName = param.name.name;
const paramValue = getValueFromTypes(param.typeAnnotation, types);
const type =
paramValue.type === 'GenericTypeAnnotation'
? paramValue.id.name
: paramValue.type;
let returnType;
switch (type) {
case 'RootTag':
returnType = {
type: 'ReservedTypeAnnotation',
name: 'RootTag',
};
break;
case 'BooleanTypeAnnotation':
returnType = {
type: 'BooleanTypeAnnotation',
};
break;
case 'Int32':
returnType = {
type: 'Int32TypeAnnotation',
};
break;
case 'Double':
returnType = {
type: 'DoubleTypeAnnotation',
};
break;
case 'Float':
returnType = {
type: 'FloatTypeAnnotation',
};
break;
case 'StringTypeAnnotation':
returnType = {
type: 'StringTypeAnnotation',
};
break;
case 'Array':
case '$ReadOnlyArray':
if (!paramValue.type === 'GenericTypeAnnotation') {
throw new Error(
'Array and $ReadOnlyArray are GenericTypeAnnotation for array',
);
}
returnType = {
type: 'ArrayTypeAnnotation',
elementType: {
// TODO: T172453752 support complex type annotation for array element
type: paramValue.typeParameters.params[0].type,
},
};
break;
case 'ArrayTypeAnnotation':
returnType = {
type: 'ArrayTypeAnnotation',
elementType: {
// TODO: T172453752 support complex type annotation for array element
type: paramValue.elementType.type,
},
};
break;
default:
type;
throw new Error(
`Unsupported param type for method "${name}", param "${paramName}". Found ${type}`,
);
}
return {
name: paramName,
optional: false,
typeAnnotation: returnType,
};
});
return {
name,
optional,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
params,
returnTypeAnnotation: {
type: 'VoidTypeAnnotation',
},
},
};
}
function getCommands(commandTypeAST, types) {
return commandTypeAST
.filter(property => property.type === 'ObjectTypeProperty')
.map(property => buildCommandSchema(property, types))
.filter(Boolean);
}
module.exports = {
getCommands,
};

View File

@@ -0,0 +1,166 @@
/**
* 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 {
CommandParamTypeAnnotation,
CommandTypeAnnotation,
NamedShape,
} from '../../../CodegenSchema.js';
import type {TypeDeclarationMap} from '../../utils';
const {getValueFromTypes} = require('../utils.js');
// $FlowFixMe[unclear-type] there's no flowtype for ASTs
type EventTypeAST = Object;
function buildCommandSchema(
property: EventTypeAST,
types: TypeDeclarationMap,
): $ReadOnly<{
name: string,
optional: boolean,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
params: $ReadOnlyArray<{
name: string,
optional: boolean,
typeAnnotation: CommandParamTypeAnnotation,
}>,
returnTypeAnnotation: {
type: 'VoidTypeAnnotation',
},
},
}> {
const name = property.key.name;
const optional = property.optional;
const value = getValueFromTypes(property.value, types);
const firstParam = value.params[0].typeAnnotation;
if (
!(
firstParam.id != null &&
firstParam.id.type === 'QualifiedTypeIdentifier' &&
firstParam.id.qualification.name === 'React' &&
firstParam.id.id.name === 'ElementRef'
)
) {
throw new Error(
`The first argument of method ${name} must be of type React.ElementRef<>`,
);
}
const params = value.params.slice(1).map(param => {
const paramName = param.name.name;
const paramValue = getValueFromTypes(param.typeAnnotation, types);
const type =
paramValue.type === 'GenericTypeAnnotation'
? paramValue.id.name
: paramValue.type;
let returnType: CommandParamTypeAnnotation;
switch (type) {
case 'RootTag':
returnType = {
type: 'ReservedTypeAnnotation',
name: 'RootTag',
};
break;
case 'BooleanTypeAnnotation':
returnType = {
type: 'BooleanTypeAnnotation',
};
break;
case 'Int32':
returnType = {
type: 'Int32TypeAnnotation',
};
break;
case 'Double':
returnType = {
type: 'DoubleTypeAnnotation',
};
break;
case 'Float':
returnType = {
type: 'FloatTypeAnnotation',
};
break;
case 'StringTypeAnnotation':
returnType = {
type: 'StringTypeAnnotation',
};
break;
case 'Array':
case '$ReadOnlyArray':
if (!paramValue.type === 'GenericTypeAnnotation') {
throw new Error(
'Array and $ReadOnlyArray are GenericTypeAnnotation for array',
);
}
returnType = {
type: 'ArrayTypeAnnotation',
elementType: {
// TODO: T172453752 support complex type annotation for array element
type: paramValue.typeParameters.params[0].type,
},
};
break;
case 'ArrayTypeAnnotation':
returnType = {
type: 'ArrayTypeAnnotation',
elementType: {
// TODO: T172453752 support complex type annotation for array element
type: paramValue.elementType.type,
},
};
break;
default:
(type: empty);
throw new Error(
`Unsupported param type for method "${name}", param "${paramName}". Found ${type}`,
);
}
return {
name: paramName,
optional: false,
typeAnnotation: returnType,
};
});
return {
name,
optional,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
params,
returnTypeAnnotation: {
type: 'VoidTypeAnnotation',
},
},
};
}
function getCommands(
commandTypeAST: $ReadOnlyArray<EventTypeAST>,
types: TypeDeclarationMap,
): $ReadOnlyArray<NamedShape<CommandTypeAnnotation>> {
return commandTypeAST
.filter(property => property.type === 'ObjectTypeProperty')
.map(property => buildCommandSchema(property, types))
.filter(Boolean);
}
module.exports = {
getCommands,
};

View File

@@ -0,0 +1,450 @@
/**
* 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 _require = require('../../parsers-commons'),
verifyPropNotAlreadyDefined = _require.verifyPropNotAlreadyDefined;
const _require2 = require('../utils.js'),
getValueFromTypes = _require2.getValueFromTypes;
// $FlowFixMe[unsupported-variance-annotation]
function getTypeAnnotationForArray(
name,
typeAnnotation,
defaultValue,
types,
parser,
buildSchema,
) {
const extractedTypeAnnotation = getValueFromTypes(typeAnnotation, types);
if (extractedTypeAnnotation.type === 'NullableTypeAnnotation') {
throw new Error(
'Nested optionals such as "$ReadOnlyArray<?boolean>" are not supported, please declare optionals at the top level of value definitions as in "?$ReadOnlyArray<boolean>"',
);
}
if (
extractedTypeAnnotation.type === 'GenericTypeAnnotation' &&
parser.getTypeAnnotationName(extractedTypeAnnotation) === '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>"',
);
}
if (extractedTypeAnnotation.type === 'GenericTypeAnnotation') {
// Resolve the type alias if it's not defined inline
const objectType = getValueFromTypes(extractedTypeAnnotation, types);
if (objectType.id.name === '$ReadOnly') {
return {
type: 'ObjectTypeAnnotation',
properties: flattenProperties(
objectType.typeParameters.params[0].properties,
types,
parser,
)
.map(prop => buildSchema(prop, types, parser))
.filter(Boolean),
};
}
if (objectType.id.name === '$ReadOnlyArray') {
// We need to go yet another level deeper to resolve
// types that may be defined in a type alias
const nestedObjectType = getValueFromTypes(
objectType.typeParameters.params[0],
types,
);
return {
type: 'ArrayTypeAnnotation',
elementType: {
type: 'ObjectTypeAnnotation',
properties: flattenProperties(
nestedObjectType.typeParameters.params[0].properties,
types,
parser,
)
.map(prop => buildSchema(prop, types, parser))
.filter(Boolean),
},
};
}
}
const type =
extractedTypeAnnotation.type === 'GenericTypeAnnotation'
? parser.getTypeAnnotationName(extractedTypeAnnotation)
: extractedTypeAnnotation.type;
switch (type) {
case 'ImageSource':
return {
type: 'ReservedPropTypeAnnotation',
name: 'ImageSourcePrimitive',
};
case 'ImageRequest':
return {
type: 'ReservedPropTypeAnnotation',
name: 'ImageRequestPrimitive',
};
case 'ColorValue':
case 'ProcessedColorValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'ColorPrimitive',
};
case 'PointValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'PointPrimitive',
};
case 'EdgeInsetsValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'EdgeInsetsPrimitive',
};
case 'DimensionValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'DimensionPrimitive',
};
case 'Stringish':
return {
type: 'StringTypeAnnotation',
};
case 'Int32':
return {
type: 'Int32TypeAnnotation',
};
case 'Double':
return {
type: 'DoubleTypeAnnotation',
};
case 'Float':
return {
type: 'FloatTypeAnnotation',
};
case 'BooleanTypeAnnotation':
return {
type: 'BooleanTypeAnnotation',
};
case 'StringTypeAnnotation':
return {
type: 'StringTypeAnnotation',
};
case 'UnionTypeAnnotation':
typeAnnotation.types.reduce((lastType, currType) => {
if (lastType && currType.type !== lastType.type) {
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 === 'StringLiteralTypeAnnotation') {
return {
type: 'StringEnumTypeAnnotation',
default: defaultValue,
options: typeAnnotation.types.map(option => option.value),
};
} else if (unionType === 'NumberLiteralTypeAnnotation') {
throw new Error(
`Arrays of int enums are not supported (see: "${name}")`,
);
} else {
throw new Error(
`Unsupported union type for "${name}", received "${unionType}"`,
);
}
default:
throw new Error(`Unknown property type for "${name}": ${type}`);
}
}
function flattenProperties(typeDefinition, types, parser) {
return typeDefinition
.map(property => {
if (property.type === 'ObjectTypeProperty') {
return property;
} else if (property.type === 'ObjectTypeSpreadProperty') {
return flattenProperties(
parser.getProperties(property.argument.id.name, types),
types,
parser,
);
}
})
.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);
}
// $FlowFixMe[unsupported-variance-annotation]
function getTypeAnnotation(
name,
annotation,
defaultValue,
withNullDefault,
types,
parser,
buildSchema,
) {
const typeAnnotation = getValueFromTypes(annotation, types);
if (
typeAnnotation.type === 'GenericTypeAnnotation' &&
parser.getTypeAnnotationName(typeAnnotation) === '$ReadOnlyArray'
) {
return {
type: 'ArrayTypeAnnotation',
elementType: getTypeAnnotationForArray(
name,
typeAnnotation.typeParameters.params[0],
defaultValue,
types,
parser,
buildSchema,
),
};
}
if (
typeAnnotation.type === 'GenericTypeAnnotation' &&
parser.getTypeAnnotationName(typeAnnotation) === '$ReadOnly'
) {
return {
type: 'ObjectTypeAnnotation',
properties: flattenProperties(
typeAnnotation.typeParameters.params[0].properties,
types,
parser,
)
.map(prop => buildSchema(prop, types, parser))
.filter(Boolean),
};
}
const type =
typeAnnotation.type === 'GenericTypeAnnotation'
? parser.getTypeAnnotationName(typeAnnotation)
: typeAnnotation.type;
switch (type) {
case 'ImageSource':
return {
type: 'ReservedPropTypeAnnotation',
name: 'ImageSourcePrimitive',
};
case 'ImageRequest':
return {
type: 'ReservedPropTypeAnnotation',
name: 'ImageRequestPrimitive',
};
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 'DimensionValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'DimensionPrimitive',
};
case 'Int32':
return {
type: 'Int32TypeAnnotation',
default: defaultValue ? defaultValue : 0,
};
case 'Double':
return {
type: 'DoubleTypeAnnotation',
default: defaultValue ? defaultValue : 0,
};
case 'Float':
return {
type: 'FloatTypeAnnotation',
default: withNullDefault
? defaultValue
: defaultValue
? defaultValue
: 0,
};
case 'BooleanTypeAnnotation':
return {
type: 'BooleanTypeAnnotation',
default: withNullDefault
? defaultValue
: defaultValue == null
? false
: defaultValue,
};
case 'StringTypeAnnotation':
if (typeof defaultValue !== 'undefined') {
return {
type: 'StringTypeAnnotation',
default: defaultValue,
};
}
throw new Error(`A default string (or null) is required for "${name}"`);
case 'Stringish':
if (typeof defaultValue !== 'undefined') {
return {
type: 'StringTypeAnnotation',
default: defaultValue,
};
}
throw new Error(`A default string (or null) is required for "${name}"`);
case 'UnionTypeAnnotation':
typeAnnotation.types.reduce((lastType, currType) => {
if (lastType && currType.type !== lastType.type) {
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 === 'StringLiteralTypeAnnotation') {
return {
type: 'StringEnumTypeAnnotation',
default: defaultValue,
options: typeAnnotation.types.map(option => option.value),
};
} else if (unionType === 'NumberLiteralTypeAnnotation') {
return {
type: 'Int32EnumTypeAnnotation',
default: defaultValue,
options: typeAnnotation.types.map(option => option.value),
};
} else {
throw new Error(
`Unsupported union type for "${name}", received "${unionType}"`,
);
}
case 'ObjectTypeAnnotation':
throw new Error(
`Cannot use "${type}" type annotation for "${name}": object types must be declared using $ReadOnly<>`,
);
case 'NumberTypeAnnotation':
throw new Error(
`Cannot use "${type}" type annotation for "${name}": must use a specific numeric type like Int32, Double, or Float`,
);
case 'UnsafeMixed':
return {
type: 'MixedTypeAnnotation',
};
default:
throw new Error(
`Unknown property type for "${name}": "${type}" in the State`,
);
}
}
function getSchemaInfo(property, types) {
const name = property.key.name;
const value = getValueFromTypes(property.value, types);
let typeAnnotation =
value.type === 'NullableTypeAnnotation' ? value.typeAnnotation : value;
const optional =
value.type === 'NullableTypeAnnotation' ||
property.optional ||
(value.type === 'GenericTypeAnnotation' &&
typeAnnotation.id.name === 'WithDefault');
if (
!property.optional &&
value.type === 'GenericTypeAnnotation' &&
typeAnnotation.id.name === 'WithDefault'
) {
throw new Error(
`key ${name} must be optional if used with WithDefault<> annotation`,
);
}
if (
value.type === 'NullableTypeAnnotation' &&
typeAnnotation.type === 'GenericTypeAnnotation' &&
typeAnnotation.id.name === 'WithDefault'
) {
throw new Error(
'WithDefault<> is optional and does not need to be marked as optional. Please remove the ? annotation in front of it.',
);
}
let type = typeAnnotation.type;
if (
type === 'GenericTypeAnnotation' &&
(typeAnnotation.id.name === 'DirectEventHandler' ||
typeAnnotation.id.name === 'BubblingEventHandler')
) {
return null;
}
if (
name === 'style' &&
type === 'GenericTypeAnnotation' &&
typeAnnotation.id.name === 'ViewStyleProp'
) {
return null;
}
let defaultValue = null;
let withNullDefault = false;
if (
type === 'GenericTypeAnnotation' &&
typeAnnotation.id.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}"?`,
);
}
defaultValue = typeAnnotation.typeParameters.params[1].value;
const defaultValueType = typeAnnotation.typeParameters.params[1].type;
typeAnnotation = typeAnnotation.typeParameters.params[0];
type =
typeAnnotation.type === 'GenericTypeAnnotation'
? typeAnnotation.id.name
: typeAnnotation.type;
if (defaultValueType === 'NullLiteralTypeAnnotation') {
defaultValue = null;
withNullDefault = true;
}
}
return {
name,
optional,
typeAnnotation,
defaultValue,
withNullDefault,
};
}
module.exports = {
getSchemaInfo,
getTypeAnnotation,
flattenProperties,
};

View File

@@ -0,0 +1,490 @@
/**
* 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 {BuildSchemaFN, Parser} from '../../parser';
import type {ASTNode, PropAST, TypeDeclarationMap} from '../../utils';
const {verifyPropNotAlreadyDefined} = require('../../parsers-commons');
const {getValueFromTypes} = require('../utils.js');
// $FlowFixMe[unsupported-variance-annotation]
function getTypeAnnotationForArray<+T>(
name: string,
typeAnnotation: $FlowFixMe,
defaultValue: $FlowFixMe | null,
types: TypeDeclarationMap,
parser: Parser,
buildSchema: BuildSchemaFN<T>,
): $FlowFixMe {
const extractedTypeAnnotation = getValueFromTypes(typeAnnotation, types);
if (extractedTypeAnnotation.type === 'NullableTypeAnnotation') {
throw new Error(
'Nested optionals such as "$ReadOnlyArray<?boolean>" are not supported, please declare optionals at the top level of value definitions as in "?$ReadOnlyArray<boolean>"',
);
}
if (
extractedTypeAnnotation.type === 'GenericTypeAnnotation' &&
parser.getTypeAnnotationName(extractedTypeAnnotation) === '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>"',
);
}
if (extractedTypeAnnotation.type === 'GenericTypeAnnotation') {
// Resolve the type alias if it's not defined inline
const objectType = getValueFromTypes(extractedTypeAnnotation, types);
if (objectType.id.name === '$ReadOnly') {
return {
type: 'ObjectTypeAnnotation',
properties: flattenProperties(
objectType.typeParameters.params[0].properties,
types,
parser,
)
.map(prop => buildSchema(prop, types, parser))
.filter(Boolean),
};
}
if (objectType.id.name === '$ReadOnlyArray') {
// We need to go yet another level deeper to resolve
// types that may be defined in a type alias
const nestedObjectType = getValueFromTypes(
objectType.typeParameters.params[0],
types,
);
return {
type: 'ArrayTypeAnnotation',
elementType: {
type: 'ObjectTypeAnnotation',
properties: flattenProperties(
nestedObjectType.typeParameters.params[0].properties,
types,
parser,
)
.map(prop => buildSchema(prop, types, parser))
.filter(Boolean),
},
};
}
}
const type =
extractedTypeAnnotation.type === 'GenericTypeAnnotation'
? parser.getTypeAnnotationName(extractedTypeAnnotation)
: extractedTypeAnnotation.type;
switch (type) {
case 'ImageSource':
return {
type: 'ReservedPropTypeAnnotation',
name: 'ImageSourcePrimitive',
};
case 'ImageRequest':
return {
type: 'ReservedPropTypeAnnotation',
name: 'ImageRequestPrimitive',
};
case 'ColorValue':
case 'ProcessedColorValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'ColorPrimitive',
};
case 'PointValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'PointPrimitive',
};
case 'EdgeInsetsValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'EdgeInsetsPrimitive',
};
case 'DimensionValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'DimensionPrimitive',
};
case 'Stringish':
return {
type: 'StringTypeAnnotation',
};
case 'Int32':
return {
type: 'Int32TypeAnnotation',
};
case 'Double':
return {
type: 'DoubleTypeAnnotation',
};
case 'Float':
return {
type: 'FloatTypeAnnotation',
};
case 'BooleanTypeAnnotation':
return {
type: 'BooleanTypeAnnotation',
};
case 'StringTypeAnnotation':
return {
type: 'StringTypeAnnotation',
};
case 'UnionTypeAnnotation':
typeAnnotation.types.reduce((lastType, currType) => {
if (lastType && currType.type !== lastType.type) {
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 === 'StringLiteralTypeAnnotation') {
return {
type: 'StringEnumTypeAnnotation',
default: (defaultValue: string),
options: typeAnnotation.types.map(option => option.value),
};
} else if (unionType === 'NumberLiteralTypeAnnotation') {
throw new Error(
`Arrays of int enums are not supported (see: "${name}")`,
);
} else {
throw new Error(
`Unsupported union type for "${name}", received "${unionType}"`,
);
}
default:
throw new Error(`Unknown property type for "${name}": ${type}`);
}
}
function flattenProperties(
typeDefinition: $ReadOnlyArray<PropAST>,
types: TypeDeclarationMap,
parser: Parser,
): $ReadOnlyArray<PropAST> {
return typeDefinition
.map(property => {
if (property.type === 'ObjectTypeProperty') {
return property;
} else if (property.type === 'ObjectTypeSpreadProperty') {
return flattenProperties(
parser.getProperties(property.argument.id.name, types),
types,
parser,
);
}
})
.reduce((acc: Array<PropAST>, 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);
}
// $FlowFixMe[unsupported-variance-annotation]
function getTypeAnnotation<+T>(
name: string,
annotation: $FlowFixMe | ASTNode,
defaultValue: $FlowFixMe | null,
withNullDefault: boolean,
types: TypeDeclarationMap,
parser: Parser,
buildSchema: BuildSchemaFN<T>,
): $FlowFixMe {
const typeAnnotation = getValueFromTypes(annotation, types);
if (
typeAnnotation.type === 'GenericTypeAnnotation' &&
parser.getTypeAnnotationName(typeAnnotation) === '$ReadOnlyArray'
) {
return {
type: 'ArrayTypeAnnotation',
elementType: getTypeAnnotationForArray(
name,
typeAnnotation.typeParameters.params[0],
defaultValue,
types,
parser,
buildSchema,
),
};
}
if (
typeAnnotation.type === 'GenericTypeAnnotation' &&
parser.getTypeAnnotationName(typeAnnotation) === '$ReadOnly'
) {
return {
type: 'ObjectTypeAnnotation',
properties: flattenProperties(
typeAnnotation.typeParameters.params[0].properties,
types,
parser,
)
.map(prop => buildSchema(prop, types, parser))
.filter(Boolean),
};
}
const type =
typeAnnotation.type === 'GenericTypeAnnotation'
? parser.getTypeAnnotationName(typeAnnotation)
: typeAnnotation.type;
switch (type) {
case 'ImageSource':
return {
type: 'ReservedPropTypeAnnotation',
name: 'ImageSourcePrimitive',
};
case 'ImageRequest':
return {
type: 'ReservedPropTypeAnnotation',
name: 'ImageRequestPrimitive',
};
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 'DimensionValue':
return {
type: 'ReservedPropTypeAnnotation',
name: 'DimensionPrimitive',
};
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 'BooleanTypeAnnotation':
return {
type: 'BooleanTypeAnnotation',
default: withNullDefault
? (defaultValue: boolean | null)
: ((defaultValue == null ? false : defaultValue): boolean),
};
case 'StringTypeAnnotation':
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 'UnionTypeAnnotation':
typeAnnotation.types.reduce((lastType, currType) => {
if (lastType && currType.type !== lastType.type) {
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 === 'StringLiteralTypeAnnotation') {
return {
type: 'StringEnumTypeAnnotation',
default: (defaultValue: string),
options: typeAnnotation.types.map(option => option.value),
};
} else if (unionType === 'NumberLiteralTypeAnnotation') {
return {
type: 'Int32EnumTypeAnnotation',
default: (defaultValue: number),
options: typeAnnotation.types.map(option => option.value),
};
} else {
throw new Error(
`Unsupported union type for "${name}", received "${unionType}"`,
);
}
case 'ObjectTypeAnnotation':
throw new Error(
`Cannot use "${type}" type annotation for "${name}": object types must be declared using $ReadOnly<>`,
);
case 'NumberTypeAnnotation':
throw new Error(
`Cannot use "${type}" type annotation for "${name}": must use a specific numeric type like Int32, Double, or Float`,
);
case 'UnsafeMixed':
return {
type: 'MixedTypeAnnotation',
};
default:
throw new Error(
`Unknown property type for "${name}": "${type}" in the State`,
);
}
}
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.value, types);
let typeAnnotation =
value.type === 'NullableTypeAnnotation' ? value.typeAnnotation : value;
const optional =
value.type === 'NullableTypeAnnotation' ||
property.optional ||
(value.type === 'GenericTypeAnnotation' &&
typeAnnotation.id.name === 'WithDefault');
if (
!property.optional &&
value.type === 'GenericTypeAnnotation' &&
typeAnnotation.id.name === 'WithDefault'
) {
throw new Error(
`key ${name} must be optional if used with WithDefault<> annotation`,
);
}
if (
value.type === 'NullableTypeAnnotation' &&
typeAnnotation.type === 'GenericTypeAnnotation' &&
typeAnnotation.id.name === 'WithDefault'
) {
throw new Error(
'WithDefault<> is optional and does not need to be marked as optional. Please remove the ? annotation in front of it.',
);
}
let type = typeAnnotation.type;
if (
type === 'GenericTypeAnnotation' &&
(typeAnnotation.id.name === 'DirectEventHandler' ||
typeAnnotation.id.name === 'BubblingEventHandler')
) {
return null;
}
if (
name === 'style' &&
type === 'GenericTypeAnnotation' &&
typeAnnotation.id.name === 'ViewStyleProp'
) {
return null;
}
let defaultValue = null;
let withNullDefault = false;
if (
type === 'GenericTypeAnnotation' &&
typeAnnotation.id.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}"?`,
);
}
defaultValue = typeAnnotation.typeParameters.params[1].value;
const defaultValueType = typeAnnotation.typeParameters.params[1].type;
typeAnnotation = typeAnnotation.typeParameters.params[0];
type =
typeAnnotation.type === 'GenericTypeAnnotation'
? typeAnnotation.id.name
: typeAnnotation.type;
if (defaultValueType === 'NullLiteralTypeAnnotation') {
defaultValue = null;
withNullDefault = true;
}
}
return {
name,
optional,
typeAnnotation,
defaultValue,
withNullDefault,
};
}
module.exports = {
getSchemaInfo,
getTypeAnnotation,
flattenProperties,
};

View File

@@ -0,0 +1,256 @@
/**
* 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 _require = require('../../error-utils'),
throwIfArgumentPropsAreNull = _require.throwIfArgumentPropsAreNull,
throwIfBubblingTypeIsNull = _require.throwIfBubblingTypeIsNull,
throwIfEventHasNoName = _require.throwIfEventHasNoName;
const _require2 = require('../../parsers-commons'),
buildPropertiesForEvent = _require2.buildPropertiesForEvent,
emitBuildEventSchema = _require2.emitBuildEventSchema,
getEventArgument = _require2.getEventArgument,
handleEventHandler = _require2.handleEventHandler;
const _require3 = require('../../parsers-primitives'),
emitBoolProp = _require3.emitBoolProp,
emitDoubleProp = _require3.emitDoubleProp,
emitFloatProp = _require3.emitFloatProp,
emitInt32Prop = _require3.emitInt32Prop,
emitMixedProp = _require3.emitMixedProp,
emitObjectProp = _require3.emitObjectProp,
emitStringProp = _require3.emitStringProp,
emitUnionProp = _require3.emitUnionProp;
function getPropertyType(
/* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's
* LTI update could not be added via codemod */
name,
optional,
typeAnnotation,
parser,
) {
const type = extractTypeFromTypeAnnotation(typeAnnotation, parser);
switch (type) {
case 'BooleanTypeAnnotation':
return emitBoolProp(name, optional);
case 'StringTypeAnnotation':
return emitStringProp(name, optional);
case 'Int32':
return emitInt32Prop(name, optional);
case 'Double':
return emitDoubleProp(name, optional);
case 'Float':
return emitFloatProp(name, optional);
case '$ReadOnly':
return getPropertyType(
name,
optional,
typeAnnotation.typeParameters.params[0],
parser,
);
case 'ObjectTypeAnnotation':
return emitObjectProp(
name,
optional,
parser,
typeAnnotation,
extractArrayElementType,
);
case 'UnionTypeAnnotation':
return emitUnionProp(name, optional, parser, typeAnnotation);
case 'UnsafeMixed':
return emitMixedProp(name, optional);
case 'ArrayTypeAnnotation':
case '$ReadOnlyArray':
return {
name,
optional,
typeAnnotation: extractArrayElementType(typeAnnotation, name, parser),
};
default:
throw new Error(`Unable to determine event type for "${name}": ${type}`);
}
}
function extractArrayElementType(typeAnnotation, name, parser) {
const type = extractTypeFromTypeAnnotation(typeAnnotation, parser);
switch (type) {
case 'BooleanTypeAnnotation':
return {
type: 'BooleanTypeAnnotation',
};
case 'StringTypeAnnotation':
return {
type: 'StringTypeAnnotation',
};
case 'Int32':
return {
type: 'Int32TypeAnnotation',
};
case 'Float':
return {
type: 'FloatTypeAnnotation',
};
case 'NumberTypeAnnotation':
case 'Double':
return {
type: 'DoubleTypeAnnotation',
};
case 'UnionTypeAnnotation':
return {
type: 'StringEnumTypeAnnotation',
options: typeAnnotation.types.map(option =>
parser.getLiteralValue(option),
),
};
case 'UnsafeMixed':
return {
type: 'MixedTypeAnnotation',
};
case 'ObjectTypeAnnotation':
return {
type: 'ObjectTypeAnnotation',
properties: parser
.getObjectProperties(typeAnnotation)
.map(member =>
buildPropertiesForEvent(member, parser, getPropertyType),
),
};
case 'ArrayTypeAnnotation':
return {
type: 'ArrayTypeAnnotation',
elementType: extractArrayElementType(
typeAnnotation.elementType,
name,
parser,
),
};
case '$ReadOnlyArray':
const genericParams = typeAnnotation.typeParameters.params;
if (genericParams.length !== 1) {
throw new Error(
`Events only supports arrays with 1 Generic type. Found ${
genericParams.length
} types:\n${prettify(genericParams)}`,
);
}
return {
type: 'ArrayTypeAnnotation',
elementType: extractArrayElementType(genericParams[0], name, parser),
};
default:
throw new Error(
`Unrecognized ${type} for Array ${name} in events.\n${prettify(
typeAnnotation,
)}`,
);
}
}
function prettify(jsonObject) {
return JSON.stringify(jsonObject, null, 2);
}
function extractTypeFromTypeAnnotation(typeAnnotation, parser) {
return typeAnnotation.type === 'GenericTypeAnnotation'
? parser.getTypeAnnotationName(typeAnnotation)
: typeAnnotation.type;
}
function findEventArgumentsAndType(
parser,
typeAnnotation,
types,
bubblingType,
paperName,
) {
throwIfEventHasNoName(typeAnnotation, parser);
const name = parser.getTypeAnnotationName(typeAnnotation);
if (name === '$ReadOnly') {
return {
argumentProps: typeAnnotation.typeParameters.params[0].properties,
paperTopLevelNameDeprecated: paperName,
bubblingType,
};
} else if (name === 'BubblingEventHandler' || name === 'DirectEventHandler') {
return handleEventHandler(
name,
typeAnnotation,
parser,
types,
findEventArgumentsAndType,
);
} else if (types[name]) {
return findEventArgumentsAndType(
parser,
types[name].right,
types,
bubblingType,
paperName,
);
} else {
return {
argumentProps: null,
bubblingType: null,
paperTopLevelNameDeprecated: null,
};
}
}
function buildEventSchema(types, property, parser) {
const name = property.key.name;
const optional =
property.optional || property.value.type === 'NullableTypeAnnotation';
let typeAnnotation =
property.value.type === 'NullableTypeAnnotation'
? property.value.typeAnnotation
: property.value;
if (
typeAnnotation.type !== 'GenericTypeAnnotation' ||
(parser.getTypeAnnotationName(typeAnnotation) !== 'BubblingEventHandler' &&
parser.getTypeAnnotationName(typeAnnotation) !== 'DirectEventHandler')
) {
return null;
}
const _findEventArgumentsAn = findEventArgumentsAndType(
parser,
typeAnnotation,
types,
),
argumentProps = _findEventArgumentsAn.argumentProps,
bubblingType = _findEventArgumentsAn.bubblingType,
paperTopLevelNameDeprecated =
_findEventArgumentsAn.paperTopLevelNameDeprecated;
const nonNullableArgumentProps = throwIfArgumentPropsAreNull(
argumentProps,
name,
);
const nonNullableBubblingType = throwIfBubblingTypeIsNull(bubblingType, name);
const argument = getEventArgument(
nonNullableArgumentProps,
parser,
getPropertyType,
);
return emitBuildEventSchema(
paperTopLevelNameDeprecated,
name,
optional,
nonNullableBubblingType,
argument,
);
}
// $FlowFixMe[unclear-type] there's no flowtype for ASTs
function getEvents(eventTypeAST, types, parser) {
return eventTypeAST
.filter(property => property.type === 'ObjectTypeProperty')
.map(property => buildEventSchema(types, property, parser))
.filter(Boolean);
}
module.exports = {
getEvents,
extractArrayElementType,
};

View File

@@ -0,0 +1,287 @@
/**
* 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 {
EventTypeAnnotation,
EventTypeShape,
NamedShape,
} from '../../../CodegenSchema.js';
import type {Parser} from '../../parser';
import type {EventArgumentReturnType} from '../../parsers-commons';
const {
throwIfArgumentPropsAreNull,
throwIfBubblingTypeIsNull,
throwIfEventHasNoName,
} = require('../../error-utils');
const {
buildPropertiesForEvent,
emitBuildEventSchema,
getEventArgument,
handleEventHandler,
} = require('../../parsers-commons');
const {
emitBoolProp,
emitDoubleProp,
emitFloatProp,
emitInt32Prop,
emitMixedProp,
emitObjectProp,
emitStringProp,
emitUnionProp,
} = require('../../parsers-primitives');
function getPropertyType(
/* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's
* LTI update could not be added via codemod */
name: string,
optional: boolean,
typeAnnotation: $FlowFixMe,
parser: Parser,
): NamedShape<EventTypeAnnotation> {
const type = extractTypeFromTypeAnnotation(typeAnnotation, parser);
switch (type) {
case 'BooleanTypeAnnotation':
return emitBoolProp(name, optional);
case 'StringTypeAnnotation':
return emitStringProp(name, optional);
case 'Int32':
return emitInt32Prop(name, optional);
case 'Double':
return emitDoubleProp(name, optional);
case 'Float':
return emitFloatProp(name, optional);
case '$ReadOnly':
return getPropertyType(
name,
optional,
typeAnnotation.typeParameters.params[0],
parser,
);
case 'ObjectTypeAnnotation':
return emitObjectProp(
name,
optional,
parser,
typeAnnotation,
extractArrayElementType,
);
case 'UnionTypeAnnotation':
return emitUnionProp(name, optional, parser, typeAnnotation);
case 'UnsafeMixed':
return emitMixedProp(name, optional);
case 'ArrayTypeAnnotation':
case '$ReadOnlyArray':
return {
name,
optional,
typeAnnotation: extractArrayElementType(typeAnnotation, name, parser),
};
default:
throw new Error(`Unable to determine event type for "${name}": ${type}`);
}
}
function extractArrayElementType(
typeAnnotation: $FlowFixMe,
name: string,
parser: Parser,
): EventTypeAnnotation {
const type = extractTypeFromTypeAnnotation(typeAnnotation, parser);
switch (type) {
case 'BooleanTypeAnnotation':
return {type: 'BooleanTypeAnnotation'};
case 'StringTypeAnnotation':
return {type: 'StringTypeAnnotation'};
case 'Int32':
return {type: 'Int32TypeAnnotation'};
case 'Float':
return {type: 'FloatTypeAnnotation'};
case 'NumberTypeAnnotation':
case 'Double':
return {
type: 'DoubleTypeAnnotation',
};
case 'UnionTypeAnnotation':
return {
type: 'StringEnumTypeAnnotation',
options: typeAnnotation.types.map(option =>
parser.getLiteralValue(option),
),
};
case 'UnsafeMixed':
return {type: 'MixedTypeAnnotation'};
case 'ObjectTypeAnnotation':
return {
type: 'ObjectTypeAnnotation',
properties: parser
.getObjectProperties(typeAnnotation)
.map(member =>
buildPropertiesForEvent(member, parser, getPropertyType),
),
};
case 'ArrayTypeAnnotation':
return {
type: 'ArrayTypeAnnotation',
elementType: extractArrayElementType(
typeAnnotation.elementType,
name,
parser,
),
};
case '$ReadOnlyArray':
const genericParams = typeAnnotation.typeParameters.params;
if (genericParams.length !== 1) {
throw new Error(
`Events only supports arrays with 1 Generic type. Found ${
genericParams.length
} types:\n${prettify(genericParams)}`,
);
}
return {
type: 'ArrayTypeAnnotation',
elementType: extractArrayElementType(genericParams[0], name, parser),
};
default:
throw new Error(
`Unrecognized ${type} for Array ${name} in events.\n${prettify(
typeAnnotation,
)}`,
);
}
}
function prettify(jsonObject: $FlowFixMe): string {
return JSON.stringify(jsonObject, null, 2);
}
function extractTypeFromTypeAnnotation(
typeAnnotation: $FlowFixMe,
parser: Parser,
): string {
return typeAnnotation.type === 'GenericTypeAnnotation'
? parser.getTypeAnnotationName(typeAnnotation)
: typeAnnotation.type;
}
function findEventArgumentsAndType(
parser: Parser,
typeAnnotation: $FlowFixMe,
types: TypeMap,
bubblingType: void | 'direct' | 'bubble',
paperName: ?$FlowFixMe,
): EventArgumentReturnType {
throwIfEventHasNoName(typeAnnotation, parser);
const name = parser.getTypeAnnotationName(typeAnnotation);
if (name === '$ReadOnly') {
return {
argumentProps: typeAnnotation.typeParameters.params[0].properties,
paperTopLevelNameDeprecated: paperName,
bubblingType,
};
} else if (name === 'BubblingEventHandler' || name === 'DirectEventHandler') {
return handleEventHandler(
name,
typeAnnotation,
parser,
types,
findEventArgumentsAndType,
);
} else if (types[name]) {
return findEventArgumentsAndType(
parser,
types[name].right,
types,
bubblingType,
paperName,
);
} else {
return {
argumentProps: null,
bubblingType: null,
paperTopLevelNameDeprecated: null,
};
}
}
function buildEventSchema(
types: TypeMap,
property: EventTypeAST,
parser: Parser,
): ?EventTypeShape {
const name = property.key.name;
const optional =
property.optional || property.value.type === 'NullableTypeAnnotation';
let typeAnnotation =
property.value.type === 'NullableTypeAnnotation'
? property.value.typeAnnotation
: property.value;
if (
typeAnnotation.type !== 'GenericTypeAnnotation' ||
(parser.getTypeAnnotationName(typeAnnotation) !== 'BubblingEventHandler' &&
parser.getTypeAnnotationName(typeAnnotation) !== 'DirectEventHandler')
) {
return null;
}
const {argumentProps, bubblingType, paperTopLevelNameDeprecated} =
findEventArgumentsAndType(parser, typeAnnotation, types);
const nonNullableArgumentProps = throwIfArgumentPropsAreNull(
argumentProps,
name,
);
const nonNullableBubblingType = throwIfBubblingTypeIsNull(bubblingType, name);
const argument = getEventArgument(
nonNullableArgumentProps,
parser,
getPropertyType,
);
return emitBuildEventSchema(
paperTopLevelNameDeprecated,
name,
optional,
nonNullableBubblingType,
argument,
);
}
// $FlowFixMe[unclear-type] there's no flowtype for ASTs
type EventTypeAST = Object;
type TypeMap = {
// $FlowFixMe[unclear-type] there's no flowtype for ASTs
[string]: Object,
...
};
function getEvents(
eventTypeAST: $ReadOnlyArray<EventTypeAST>,
types: TypeMap,
parser: Parser,
): $ReadOnlyArray<EventTypeShape> {
return eventTypeAST
.filter(property => property.type === 'ObjectTypeProperty')
.map(property => buildEventSchema(types, property, parser))
.filter(Boolean);
}
module.exports = {
getEvents,
extractArrayElementType,
};

View File

@@ -0,0 +1,49 @@
/**
* 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 _require = require('../../parsers-commons'),
findComponentConfig = _require.findComponentConfig,
getCommandProperties = _require.getCommandProperties,
getOptions = _require.getOptions;
const _require2 = require('./commands'),
getCommands = _require2.getCommands;
const _require3 = require('./events'),
getEvents = _require3.getEvents;
// $FlowFixMe[signature-verification-failure] there's no flowtype for AST
function buildComponentSchema(ast, parser) {
const _findComponentConfig = findComponentConfig(ast, parser),
componentName = _findComponentConfig.componentName,
propsTypeName = _findComponentConfig.propsTypeName,
optionsExpression = _findComponentConfig.optionsExpression;
const types = parser.getTypes(ast);
const propProperties = parser.getProperties(propsTypeName, types);
const commandProperties = getCommandProperties(ast, parser);
const _parser$getProps = parser.getProps(propProperties, types),
extendsProps = _parser$getProps.extendsProps,
props = _parser$getProps.props;
const options = getOptions(optionsExpression);
const events = getEvents(propProperties, types, parser);
const commands = getCommands(commandProperties, types);
return {
filename: componentName,
componentName,
options,
extendsProps,
events,
props,
commands,
};
}
module.exports = {
buildComponentSchema,
};

View File

@@ -0,0 +1,56 @@
/**
* 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 {Parser} from '../../parser';
import type {ComponentSchemaBuilderConfig} from '../../schema.js';
const {
findComponentConfig,
getCommandProperties,
getOptions,
} = require('../../parsers-commons');
const {getCommands} = require('./commands');
const {getEvents} = require('./events');
// $FlowFixMe[signature-verification-failure] there's no flowtype for AST
function buildComponentSchema(
ast: $FlowFixMe,
parser: Parser,
): ComponentSchemaBuilderConfig {
const {componentName, propsTypeName, optionsExpression} = findComponentConfig(
ast,
parser,
);
const types = parser.getTypes(ast);
const propProperties = parser.getProperties(propsTypeName, types);
const commandProperties = getCommandProperties(ast, parser);
const {extendsProps, props} = parser.getProps(propProperties, types);
const options = getOptions(optionsExpression);
const events = getEvents(propProperties, types, parser);
const commands = getCommands(commandProperties, types);
return {
filename: componentName,
componentName,
options,
extendsProps,
events,
props,
commands,
};
}
module.exports = {
buildComponentSchema,
};

View File

@@ -0,0 +1,269 @@
/**
* 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
*/
// @licenselint-loose-mode
'use strict';
const NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
getString: (arg: string) => Array;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT_AS_PARAM = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
getString: (arg : Array) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_READ_ONLY_OBJECT_NO_TYPE_FOR_CONTENT = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
getString: (arg : $ReadOnly<>) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_NOT_ONLY_METHODS = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getBool: (arg: boolean) => boolean;
+getNumber: (arg: number) => number;
+getString: (arg: string) => string;
sampleBool: boolean,
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_UNNAMED_PARAMS = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getBool: (boolean) => boolean;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_PROMISE_WITHOUT_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.
*
* @flow strict-local
* @format
*/
'use strict';
import type {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getBool: (arg: boolean) => Promise;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const TWO_NATIVE_MODULES_EXPORTED_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.
*
* @flow strict-local
* @format
*/
'use strict';
import type {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export default TurboModuleRegistry.getEnforcing<Spec1>('SampleTurboModule1');
export default TurboModuleRegistry.getEnforcing<Spec2>('SampleTurboModule2');
`;
const TWO_NATIVE_EXTENDING_TURBO_MODULE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getSth: (a : ?number) => void
}
export interface Spec2 extends TurboModule {
+getSth: (a : ?number) => void
}
`;
const EMPTY_ENUM_NATIVE_MODULE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export enum SomeEnum {
}
export interface Spec extends TurboModule {
+getEnums: (a: SomeEnum) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('EmptyEnumNativeModule');
`;
const MIXED_VALUES_ENUM_NATIVE_MODULE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export enum SomeEnum {
NUM = 1,
STR = 'str',
}
export interface Spec extends TurboModule {
+getEnums: (a: SomeEnum) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('MixedValuesEnumNativeModule');
`;
module.exports = {
NATIVE_MODULES_WITH_READ_ONLY_OBJECT_NO_TYPE_FOR_CONTENT,
NATIVE_MODULES_WITH_UNNAMED_PARAMS,
NATIVE_MODULES_WITH_PROMISE_WITHOUT_TYPE,
NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT_AS_PARAM,
NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT,
TWO_NATIVE_MODULES_EXPORTED_WITH_DEFAULT,
NATIVE_MODULES_WITH_NOT_ONLY_METHODS,
TWO_NATIVE_EXTENDING_TURBO_MODULE,
EMPTY_ENUM_NATIVE_MODULE,
MIXED_VALUES_ENUM_NATIVE_MODULE,
};

View File

@@ -0,0 +1,279 @@
/**
* 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
*/
// @licenselint-loose-mode
'use strict';
const NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
getString: (arg: string) => Array;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT_AS_PARAM = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
getString: (arg : Array) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_READ_ONLY_OBJECT_NO_TYPE_FOR_CONTENT = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
getString: (arg : $ReadOnly<>) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_NOT_ONLY_METHODS = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getBool: (arg: boolean) => boolean;
+getNumber: (arg: number) => number;
+getString: (arg: string) => string;
sampleBool: boolean,
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_UNNAMED_PARAMS = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getBool: (boolean) => boolean;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_PROMISE_WITHOUT_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.
*
* @flow strict-local
* @format
*/
'use strict';
import type {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getBool: (arg: boolean) => Promise;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const TWO_NATIVE_MODULES_EXPORTED_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.
*
* @flow strict-local
* @format
*/
'use strict';
import type {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export default TurboModuleRegistry.getEnforcing<Spec1>('SampleTurboModule1');
export default TurboModuleRegistry.getEnforcing<Spec2>('SampleTurboModule2');
`;
const TWO_NATIVE_EXTENDING_TURBO_MODULE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getSth: (a : ?number) => void
}
export interface Spec2 extends TurboModule {
+getSth: (a : ?number) => void
}
`;
const EMPTY_ENUM_NATIVE_MODULE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export enum SomeEnum {
}
export interface Spec extends TurboModule {
+getEnums: (a: SomeEnum) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('EmptyEnumNativeModule');
`;
const MIXED_VALUES_ENUM_NATIVE_MODULE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export enum SomeEnum {
NUM = 1,
STR = 'str',
}
export interface Spec extends TurboModule {
+getEnums: (a: SomeEnum) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('MixedValuesEnumNativeModule');
`;
module.exports = {
NATIVE_MODULES_WITH_READ_ONLY_OBJECT_NO_TYPE_FOR_CONTENT,
NATIVE_MODULES_WITH_UNNAMED_PARAMS,
NATIVE_MODULES_WITH_PROMISE_WITHOUT_TYPE,
NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT_AS_PARAM,
NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT,
TWO_NATIVE_MODULES_EXPORTED_WITH_DEFAULT,
NATIVE_MODULES_WITH_NOT_ONLY_METHODS,
TWO_NATIVE_EXTENDING_TURBO_MODULE,
EMPTY_ENUM_NATIVE_MODULE,
MIXED_VALUES_ENUM_NATIVE_MODULE,
};

View File

@@ -0,0 +1,800 @@
/**
* 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
*/
// @licenselint-loose-mode
'use strict';
const EMPTY_NATIVE_MODULE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
// no methods
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_COMPLEX_OBJECTS = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type String = string;
export interface Spec extends TurboModule {
// Exported methods.
+getObject: (arg: {|const1: {|const1: boolean|}|}) => {|
const1: {|const1: boolean|},
|};
+getReadOnlyObject: (arg: $ReadOnly<{|const1: $ReadOnly<{|const1: boolean|}>|}>) => $ReadOnly<{|
const1: {|const1: boolean|},
|}>;
+getObject2: (arg: { a: String }) => Object;
+getObjectInArray: (arg: {const1: {|const1: boolean|}}) => Array<{|
const1: {const1: boolean},
|}>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_COMPLEX_OBJECTS_WITH_NULLABLE_KEY = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getConstants: () => {|
isTesting: boolean,
reactNativeVersion: {|
major: number,
minor: number,
patch?: number,
prerelease: ?number,
|},
forceTouchAvailable: boolean,
osVersion: string,
systemName: string,
interfaceIdiom: string,
|};
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_BASIC_PARAM_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.
*
* @flow strict-local
* @format
*/
'use strict';
import type {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+passBool?: (arg: boolean) => void;
+passNumber: (arg: number) => void;
+passString: (arg: string) => void;
+passStringish: (arg: Stringish) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_ALIASES = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
type NumNum = number;
export type Num = (arg: NumNum) => void;
type Num2 = Num;
export type Void = void;
export type A = number;
export type B = number;
export type ObjectAlias = {|
x: number,
y: number,
label: string,
truthy: boolean,
|};
export type ReadOnlyAlias = $ReadOnly<ObjectAlias>;
export interface Spec extends TurboModule {
// Exported methods.
+getNumber: Num2;
+getVoid: () => Void;
+getArray: (a: Array<A>) => {| a: B |};
+getStringFromAlias: (a: ObjectAlias) => string;
+getStringFromNullableAlias: (a: ?ObjectAlias) => string;
+getStringFromReadOnlyAlias: (a: ReadOnlyAlias) => string;
+getStringFromNullableReadOnlyAlias: (a: ?ReadOnlyAlias) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_NESTED_ALIASES = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
type Bar = {|
z: number
|};
type Foo = {|
bar1: Bar,
bar2: Bar,
|};
export interface Spec extends TurboModule {
// Exported methods.
foo1: (x: Foo) => Foo;
foo2: (x: Foo) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_FLOAT_AND_INT32 = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
import type {Int32, Float} from 'react-native/Libraries/Types/CodegenTypes';
export interface Spec extends TurboModule {
+getInt: (arg: Int32) => Int32;
+getFloat: (arg: Float) => Float;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_SIMPLE_OBJECT = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getObject: (o: Object) => Object,
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_UNSAFE_OBJECT = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
import type {UnsafeObject} from 'react-native/Libraries/Types/CodegenTypes';
export interface Spec extends TurboModule {
+getUnsafeObject: (o: UnsafeObject) => UnsafeObject,
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_PARTIALS = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type SomeObj = {|
a: string,
b?: boolean,
|};
export interface Spec extends TurboModule {
+getSomeObj: () => SomeObj;
+getPartialSomeObj: () => Partial<SomeObj>;
+getSomeObjFromPartialSomeObj: (value: Partial<SomeObj>) => SomeObj;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_PARTIALS_COMPLEX = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type SomeObj = {|
a: string,
b?: boolean,
|};
export type PartialSomeObj = Partial<SomeObj>;
export interface Spec extends TurboModule {
+getPartialPartial: (value1: Partial<SomeObj>, value2: PartialSomeObj) => SomeObj
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_ROOT_TAG = `
/**
* 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 {RootTag, TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getRootTag: (rootTag: RootTag) => RootTag,
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_NULLABLE_PARAM = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
// Exported methods.
+voidFunc: (arg: ?string) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_BASIC_ARRAY = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getArray: (arg: Array<string>) => Array<string>;
+getArray: (arg: $ReadOnlyArray<string>) => $ReadOnlyArray<string>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_OBJECT_WITH_OBJECT_DEFINED_IN_FILE_AS_PROPERTY = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
type DisplayMetricsAndroid = {|
width: number,
|};
export interface Spec extends TurboModule {
+getConstants: () => {|
+Dimensions: {
windowPhysicalPixels: DisplayMetricsAndroid,
},
|};
+getConstants2: () => $ReadOnly<{|
+Dimensions: {
windowPhysicalPixels: DisplayMetricsAndroid,
},
|}>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_ARRAY_WITH_UNION_AND_TOUPLE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getArray: (arg: Array<[string, string]>) => Array<string | number | boolean>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_ARRAY_WITH_ALIAS = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type SomeString = string;
export interface Spec extends TurboModule {
+getArray: (arg: Array<SomeString>) => Array<string>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_COMPLEX_ARRAY = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getArray: (arg: Array<Array<Array<Array<Array<string>>>>>) => Array<Array<Array<string>>>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_PROMISE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type String = string;
export type SomeObj = {| a: string |};
export interface Spec extends TurboModule {
+getValueWithPromise: () => Promise<string>;
+getValueWithPromiseDefinedSomewhereElse: () => Promise<String>;
+getValueWithPromiseObjDefinedSomewhereElse: () => Promise<SomeObj>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_CALLBACK = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
// Exported methods.
+getValueWithCallback: (
callback: (value: string, arr: Array<Array<string>>) => void,
) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_UNION = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type ChooseInt = 1 | 2 | 3;
export type ChooseFloat = 1.44 | 2.88 | 5.76;
export type ChooseObject = {} | {low: string};
export type ChooseString = 'One' | 'Two' | 'Three';
export interface Spec extends TurboModule {
+getUnion: (chooseInt: ChooseInt, chooseFloat: ChooseFloat, chooseObject: ChooseObject, chooseString: ChooseString) => ChooseObject;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const ANDROID_ONLY_NATIVE_MODULE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
// no methods
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModuleAndroid');
`;
const IOS_ONLY_NATIVE_MODULE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export enum Quality {
SD,
HD,
}
export enum Resolution {
Low = 720,
High = 1080,
}
export enum Floppy {
LowDensity = 0.72,
HighDensity = 1.44,
}
export enum StringOptions {
One = 'one',
Two = 'two',
Three = 'three',
}
export interface Spec extends TurboModule {
getEnums(quality: Quality, resolution?: Resolution, floppy: Floppy, stringOptions: StringOptions): string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModuleIOS');
`;
const CXX_ONLY_NATIVE_MODULE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type ChooseInt = 1 | 2 | 3;
export type ChooseFloat = 1.44 | 2.88 | 5.76;
export type ChooseObject = {} | {low: string};
export type ChooseString = 'One' | 'Two' | 'Three';
export enum Quality {
SD,
HD,
}
export enum Resolution {
Low = 720,
High = 1080,
}
export enum Floppy {
LowDensity = 0.72,
HighDensity = 1.44,
}
export enum StringOptions {
One = 'one',
Two = 'two',
Three = 'three',
}
export type BinaryTreeNode = {
left?: BinaryTreeNode,
value: number,
right?: BinaryTreeNode,
};
export type GraphNode = {
label: string,
neighbors?: Array<GraphNode>,
};
export interface Spec extends TurboModule {
+getCallback: () => () => void;
+getMixed: (arg: mixed) => mixed;
+getEnums: (quality: Quality, resolution?: Resolution, floppy: Floppy, stringOptions: StringOptions) => string;
+getBinaryTreeNode: (arg: BinaryTreeNode) => BinaryTreeNode;
+getGraphNode: (arg: GraphNode) => GraphNode;
+getMap: (arg: {[a: string]: ?number}) => {[b: string]: ?number};
+getAnotherMap: (arg: {[string]: string}) => {[string]: string};
+getUnion: (chooseInt: ChooseInt, chooseFloat: ChooseFloat, chooseObject: ChooseObject, chooseString: ChooseString) => ChooseObject;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModuleCxx');
`;
const PROMISE_WITH_COMMONLY_USED_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.
*
* @flow strict-local
* @format
*/
'use strict';
import type {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type Season = 'Spring' | 'Summer' | 'Autumn' | 'Winter';
export type CustomObject = {|
field1: Array<Object>,
field2: boolean,
field3: string,
type: 'A_String_Literal',
|};
export interface Spec extends TurboModule {
returnStringArray(): Promise<Array<string>>;
returnObjectArray(): Promise<Array<Object>>;
returnNullableNumber(): Promise<number | null>;
returnEmpty(): Promise<empty>;
returnUnsupportedIndex(): Promise<{ [string]: 'authorized' | 'denied' | 'undetermined' | true | false }>;
returnSupportedIndex(): Promise<{ [string]: CustomObject }>;
returnEnum() : Promise<Season>;
returnObject() : Promise<CustomObject>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
module.exports = {
NATIVE_MODULE_WITH_OBJECT_WITH_OBJECT_DEFINED_IN_FILE_AS_PROPERTY,
NATIVE_MODULE_WITH_ARRAY_WITH_UNION_AND_TOUPLE,
NATIVE_MODULE_WITH_FLOAT_AND_INT32,
NATIVE_MODULE_WITH_ALIASES,
NATIVE_MODULE_WITH_NESTED_ALIASES,
NATIVE_MODULE_WITH_PROMISE,
NATIVE_MODULE_WITH_COMPLEX_OBJECTS,
NATIVE_MODULE_WITH_COMPLEX_OBJECTS_WITH_NULLABLE_KEY,
NATIVE_MODULE_WITH_SIMPLE_OBJECT,
NATIVE_MODULE_WITH_UNSAFE_OBJECT,
NATIVE_MODULE_WITH_PARTIALS,
NATIVE_MODULE_WITH_PARTIALS_COMPLEX,
NATIVE_MODULE_WITH_ROOT_TAG,
NATIVE_MODULE_WITH_NULLABLE_PARAM,
NATIVE_MODULE_WITH_BASIC_ARRAY,
NATIVE_MODULE_WITH_COMPLEX_ARRAY,
NATIVE_MODULE_WITH_ARRAY_WITH_ALIAS,
NATIVE_MODULE_WITH_BASIC_PARAM_TYPES,
NATIVE_MODULE_WITH_CALLBACK,
NATIVE_MODULE_WITH_UNION,
EMPTY_NATIVE_MODULE,
ANDROID_ONLY_NATIVE_MODULE,
IOS_ONLY_NATIVE_MODULE,
CXX_ONLY_NATIVE_MODULE,
PROMISE_WITH_COMMONLY_USED_TYPES,
};

View File

@@ -0,0 +1,825 @@
/**
* 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
*/
// @licenselint-loose-mode
'use strict';
const EMPTY_NATIVE_MODULE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
// no methods
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_COMPLEX_OBJECTS = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type String = string;
export interface Spec extends TurboModule {
// Exported methods.
+getObject: (arg: {|const1: {|const1: boolean|}|}) => {|
const1: {|const1: boolean|},
|};
+getReadOnlyObject: (arg: $ReadOnly<{|const1: $ReadOnly<{|const1: boolean|}>|}>) => $ReadOnly<{|
const1: {|const1: boolean|},
|}>;
+getObject2: (arg: { a: String }) => Object;
+getObjectInArray: (arg: {const1: {|const1: boolean|}}) => Array<{|
const1: {const1: boolean},
|}>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_COMPLEX_OBJECTS_WITH_NULLABLE_KEY = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getConstants: () => {|
isTesting: boolean,
reactNativeVersion: {|
major: number,
minor: number,
patch?: number,
prerelease: ?number,
|},
forceTouchAvailable: boolean,
osVersion: string,
systemName: string,
interfaceIdiom: string,
|};
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_BASIC_PARAM_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.
*
* @flow strict-local
* @format
*/
'use strict';
import type {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+passBool?: (arg: boolean) => void;
+passNumber: (arg: number) => void;
+passString: (arg: string) => void;
+passStringish: (arg: Stringish) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_ALIASES = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
type NumNum = number;
export type Num = (arg: NumNum) => void;
type Num2 = Num;
export type Void = void;
export type A = number;
export type B = number;
export type ObjectAlias = {|
x: number,
y: number,
label: string,
truthy: boolean,
|};
export type ReadOnlyAlias = $ReadOnly<ObjectAlias>;
export interface Spec extends TurboModule {
// Exported methods.
+getNumber: Num2;
+getVoid: () => Void;
+getArray: (a: Array<A>) => {| a: B |};
+getStringFromAlias: (a: ObjectAlias) => string;
+getStringFromNullableAlias: (a: ?ObjectAlias) => string;
+getStringFromReadOnlyAlias: (a: ReadOnlyAlias) => string;
+getStringFromNullableReadOnlyAlias: (a: ?ReadOnlyAlias) => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_NESTED_ALIASES = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
type Bar = {|
z: number
|};
type Foo = {|
bar1: Bar,
bar2: Bar,
|};
export interface Spec extends TurboModule {
// Exported methods.
foo1: (x: Foo) => Foo;
foo2: (x: Foo) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_FLOAT_AND_INT32 = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
import type {Int32, Float} from 'react-native/Libraries/Types/CodegenTypes';
export interface Spec extends TurboModule {
+getInt: (arg: Int32) => Int32;
+getFloat: (arg: Float) => Float;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_SIMPLE_OBJECT = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getObject: (o: Object) => Object,
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_UNSAFE_OBJECT = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
import type {UnsafeObject} from 'react-native/Libraries/Types/CodegenTypes';
export interface Spec extends TurboModule {
+getUnsafeObject: (o: UnsafeObject) => UnsafeObject,
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_PARTIALS = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type SomeObj = {|
a: string,
b?: boolean,
|};
export interface Spec extends TurboModule {
+getSomeObj: () => SomeObj;
+getPartialSomeObj: () => Partial<SomeObj>;
+getSomeObjFromPartialSomeObj: (value: Partial<SomeObj>) => SomeObj;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_PARTIALS_COMPLEX = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type SomeObj = {|
a: string,
b?: boolean,
|};
export type PartialSomeObj = Partial<SomeObj>;
export interface Spec extends TurboModule {
+getPartialPartial: (value1: Partial<SomeObj>, value2: PartialSomeObj) => SomeObj
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_ROOT_TAG = `
/**
* 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 {RootTag, TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getRootTag: (rootTag: RootTag) => RootTag,
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_NULLABLE_PARAM = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
// Exported methods.
+voidFunc: (arg: ?string) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_BASIC_ARRAY = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getArray: (arg: Array<string>) => Array<string>;
+getArray: (arg: $ReadOnlyArray<string>) => $ReadOnlyArray<string>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_OBJECT_WITH_OBJECT_DEFINED_IN_FILE_AS_PROPERTY = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
type DisplayMetricsAndroid = {|
width: number,
|};
export interface Spec extends TurboModule {
+getConstants: () => {|
+Dimensions: {
windowPhysicalPixels: DisplayMetricsAndroid,
},
|};
+getConstants2: () => $ReadOnly<{|
+Dimensions: {
windowPhysicalPixels: DisplayMetricsAndroid,
},
|}>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_ARRAY_WITH_UNION_AND_TOUPLE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getArray: (arg: Array<[string, string]>) => Array<string | number | boolean>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_ARRAY_WITH_ALIAS = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type SomeString = string;
export interface Spec extends TurboModule {
+getArray: (arg: Array<SomeString>) => Array<string>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_COMPLEX_ARRAY = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+getArray: (arg: Array<Array<Array<Array<Array<string>>>>>) => Array<Array<Array<string>>>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_PROMISE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type String = string;
export type SomeObj = {| a: string |};
export interface Spec extends TurboModule {
+getValueWithPromise: () => Promise<string>;
+getValueWithPromiseDefinedSomewhereElse: () => Promise<String>;
+getValueWithPromiseObjDefinedSomewhereElse: () => Promise<SomeObj>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_CALLBACK = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
// Exported methods.
+getValueWithCallback: (
callback: (value: string, arr: Array<Array<string>>) => void,
) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_UNION = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type ChooseInt = 1 | 2 | 3;
export type ChooseFloat = 1.44 | 2.88 | 5.76;
export type ChooseObject = {} | {low: string};
export type ChooseString = 'One' | 'Two' | 'Three';
export interface Spec extends TurboModule {
+getUnion: (chooseInt: ChooseInt, chooseFloat: ChooseFloat, chooseObject: ChooseObject, chooseString: ChooseString) => ChooseObject;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const ANDROID_ONLY_NATIVE_MODULE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
// no methods
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModuleAndroid');
`;
const IOS_ONLY_NATIVE_MODULE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export enum Quality {
SD,
HD,
}
export enum Resolution {
Low = 720,
High = 1080,
}
export enum Floppy {
LowDensity = 0.72,
HighDensity = 1.44,
}
export enum StringOptions {
One = 'one',
Two = 'two',
Three = 'three',
}
export interface Spec extends TurboModule {
getEnums(quality: Quality, resolution?: Resolution, floppy: Floppy, stringOptions: StringOptions): string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModuleIOS');
`;
const CXX_ONLY_NATIVE_MODULE = `
/**
* 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 {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type ChooseInt = 1 | 2 | 3;
export type ChooseFloat = 1.44 | 2.88 | 5.76;
export type ChooseObject = {} | {low: string};
export type ChooseString = 'One' | 'Two' | 'Three';
export enum Quality {
SD,
HD,
}
export enum Resolution {
Low = 720,
High = 1080,
}
export enum Floppy {
LowDensity = 0.72,
HighDensity = 1.44,
}
export enum StringOptions {
One = 'one',
Two = 'two',
Three = 'three',
}
export type BinaryTreeNode = {
left?: BinaryTreeNode,
value: number,
right?: BinaryTreeNode,
};
export type GraphNode = {
label: string,
neighbors?: Array<GraphNode>,
};
export interface Spec extends TurboModule {
+getCallback: () => () => void;
+getMixed: (arg: mixed) => mixed;
+getEnums: (quality: Quality, resolution?: Resolution, floppy: Floppy, stringOptions: StringOptions) => string;
+getBinaryTreeNode: (arg: BinaryTreeNode) => BinaryTreeNode;
+getGraphNode: (arg: GraphNode) => GraphNode;
+getMap: (arg: {[a: string]: ?number}) => {[b: string]: ?number};
+getAnotherMap: (arg: {[string]: string}) => {[string]: string};
+getUnion: (chooseInt: ChooseInt, chooseFloat: ChooseFloat, chooseObject: ChooseObject, chooseString: ChooseString) => ChooseObject;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModuleCxx');
`;
const PROMISE_WITH_COMMONLY_USED_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.
*
* @flow strict-local
* @format
*/
'use strict';
import type {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type Season = 'Spring' | 'Summer' | 'Autumn' | 'Winter';
export type CustomObject = {|
field1: Array<Object>,
field2: boolean,
field3: string,
type: 'A_String_Literal',
|};
export interface Spec extends TurboModule {
returnStringArray(): Promise<Array<string>>;
returnObjectArray(): Promise<Array<Object>>;
returnNullableNumber(): Promise<number | null>;
returnEmpty(): Promise<empty>;
returnUnsupportedIndex(): Promise<{ [string]: 'authorized' | 'denied' | 'undetermined' | true | false }>;
returnSupportedIndex(): Promise<{ [string]: CustomObject }>;
returnEnum() : Promise<Season>;
returnObject() : Promise<CustomObject>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
module.exports = {
NATIVE_MODULE_WITH_OBJECT_WITH_OBJECT_DEFINED_IN_FILE_AS_PROPERTY,
NATIVE_MODULE_WITH_ARRAY_WITH_UNION_AND_TOUPLE,
NATIVE_MODULE_WITH_FLOAT_AND_INT32,
NATIVE_MODULE_WITH_ALIASES,
NATIVE_MODULE_WITH_NESTED_ALIASES,
NATIVE_MODULE_WITH_PROMISE,
NATIVE_MODULE_WITH_COMPLEX_OBJECTS,
NATIVE_MODULE_WITH_COMPLEX_OBJECTS_WITH_NULLABLE_KEY,
NATIVE_MODULE_WITH_SIMPLE_OBJECT,
NATIVE_MODULE_WITH_UNSAFE_OBJECT,
NATIVE_MODULE_WITH_PARTIALS,
NATIVE_MODULE_WITH_PARTIALS_COMPLEX,
NATIVE_MODULE_WITH_ROOT_TAG,
NATIVE_MODULE_WITH_NULLABLE_PARAM,
NATIVE_MODULE_WITH_BASIC_ARRAY,
NATIVE_MODULE_WITH_COMPLEX_ARRAY,
NATIVE_MODULE_WITH_ARRAY_WITH_ALIAS,
NATIVE_MODULE_WITH_BASIC_PARAM_TYPES,
NATIVE_MODULE_WITH_CALLBACK,
NATIVE_MODULE_WITH_UNION,
EMPTY_NATIVE_MODULE,
ANDROID_ONLY_NATIVE_MODULE,
IOS_ONLY_NATIVE_MODULE,
CXX_ONLY_NATIVE_MODULE,
PROMISE_WITH_COMMONLY_USED_TYPES,
};

View File

@@ -0,0 +1,317 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
'use strict';
function _slicedToArray(arr, i) {
return (
_arrayWithHoles(arr) ||
_iterableToArrayLimit(arr, i) ||
_unsupportedIterableToArray(arr, i) ||
_nonIterableRest()
);
}
function _nonIterableRest() {
throw new TypeError(
'Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.',
);
}
function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === 'string') return _arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === 'Object' && o.constructor) n = o.constructor.name;
if (n === 'Map' || n === 'Set') return Array.from(o);
if (n === 'Arguments' || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))
return _arrayLikeToArray(o, minLen);
}
function _arrayLikeToArray(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
return arr2;
}
function _iterableToArrayLimit(arr, i) {
var _i =
null == arr
? null
: ('undefined' != typeof Symbol && arr[Symbol.iterator]) ||
arr['@@iterator'];
if (null != _i) {
var _s,
_e,
_x,
_r,
_arr = [],
_n = !0,
_d = !1;
try {
if (((_x = (_i = _i.call(arr)).next), 0 === i)) {
if (Object(_i) !== _i) return;
_n = !1;
} else
for (
;
!(_n = (_s = _x.call(_i)).done) &&
(_arr.push(_s.value), _arr.length !== i);
_n = !0
);
} catch (err) {
(_d = !0), (_e = err);
} finally {
try {
if (!_n && null != _i.return && ((_r = _i.return()), Object(_r) !== _r))
return;
} finally {
if (_d) throw _e;
}
}
return _arr;
}
}
function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}
const _require = require('../../errors'),
UnsupportedGenericParserError = _require.UnsupportedGenericParserError,
UnsupportedTypeAnnotationParserError =
_require.UnsupportedTypeAnnotationParserError;
const _require2 = require('../../parsers-commons'),
assertGenericTypeAnnotationHasExactlyOneTypeParameter =
_require2.assertGenericTypeAnnotationHasExactlyOneTypeParameter,
parseObjectProperty = _require2.parseObjectProperty,
unwrapNullable = _require2.unwrapNullable,
wrapNullable = _require2.wrapNullable;
const _require3 = require('../../parsers-primitives'),
emitArrayType = _require3.emitArrayType,
emitCommonTypes = _require3.emitCommonTypes,
emitDictionary = _require3.emitDictionary,
emitFunction = _require3.emitFunction,
emitPromise = _require3.emitPromise,
emitRootTag = _require3.emitRootTag,
emitUnion = _require3.emitUnion,
typeAliasResolution = _require3.typeAliasResolution,
typeEnumResolution = _require3.typeEnumResolution;
function translateTypeAnnotation(
hasteModuleName,
/**
* TODO(T71778680): Flow-type this node.
*/
flowTypeAnnotation,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
) {
const resolveTypeAnnotationFN = parser.getResolveTypeAnnotationFN();
const _resolveTypeAnnotatio = resolveTypeAnnotationFN(
flowTypeAnnotation,
types,
parser,
),
nullable = _resolveTypeAnnotatio.nullable,
typeAnnotation = _resolveTypeAnnotatio.typeAnnotation,
typeResolutionStatus = _resolveTypeAnnotatio.typeResolutionStatus;
switch (typeAnnotation.type) {
case 'GenericTypeAnnotation': {
switch (parser.getTypeAnnotationName(typeAnnotation)) {
case 'RootTag': {
return emitRootTag(nullable);
}
case 'Promise': {
return emitPromise(
hasteModuleName,
typeAnnotation,
parser,
nullable,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
translateTypeAnnotation,
);
}
case 'Array':
case '$ReadOnlyArray': {
return emitArrayType(
hasteModuleName,
typeAnnotation,
parser,
types,
aliasMap,
enumMap,
cxxOnly,
nullable,
translateTypeAnnotation,
);
}
case '$ReadOnly': {
assertGenericTypeAnnotationHasExactlyOneTypeParameter(
hasteModuleName,
typeAnnotation,
parser,
);
const _unwrapNullable = unwrapNullable(
translateTypeAnnotation(
hasteModuleName,
typeAnnotation.typeParameters.params[0],
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
),
),
_unwrapNullable2 = _slicedToArray(_unwrapNullable, 2),
paramType = _unwrapNullable2[0],
isParamNullable = _unwrapNullable2[1];
return wrapNullable(nullable || isParamNullable, paramType);
}
default: {
const commonType = emitCommonTypes(
hasteModuleName,
types,
typeAnnotation,
aliasMap,
enumMap,
tryParse,
cxxOnly,
nullable,
parser,
);
if (!commonType) {
throw new UnsupportedGenericParserError(
hasteModuleName,
typeAnnotation,
parser,
);
}
return commonType;
}
}
}
case 'ObjectTypeAnnotation': {
// if there is any indexer, then it is a dictionary
if (typeAnnotation.indexers) {
const indexers = typeAnnotation.indexers.filter(
member => member.type === 'ObjectTypeIndexer',
);
if (indexers.length > 0) {
// check the property type to prevent developers from using unsupported types
// the return value from `translateTypeAnnotation` is unused
const propertyType = indexers[0].value;
const valueType = translateTypeAnnotation(
hasteModuleName,
propertyType,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
);
// no need to do further checking
return emitDictionary(nullable, valueType);
}
}
const objectTypeAnnotation = {
type: 'ObjectTypeAnnotation',
// $FlowFixMe[missing-type-arg]
properties: [...typeAnnotation.properties, ...typeAnnotation.indexers]
.map(property => {
return tryParse(() => {
return parseObjectProperty(
flowTypeAnnotation,
property,
hasteModuleName,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
nullable,
translateTypeAnnotation,
parser,
);
});
})
.filter(Boolean),
};
return typeAliasResolution(
typeResolutionStatus,
objectTypeAnnotation,
aliasMap,
nullable,
);
}
case 'FunctionTypeAnnotation': {
return emitFunction(
nullable,
hasteModuleName,
typeAnnotation,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
translateTypeAnnotation,
parser,
);
}
case 'UnionTypeAnnotation': {
return emitUnion(nullable, hasteModuleName, typeAnnotation, parser);
}
case 'StringLiteralTypeAnnotation': {
// 'a' is a special case for 'a' | 'b' but the type name is different
return wrapNullable(nullable, {
type: 'UnionTypeAnnotation',
memberType: 'StringTypeAnnotation',
});
}
case 'EnumStringBody':
case 'EnumNumberBody': {
return typeEnumResolution(
typeAnnotation,
typeResolutionStatus,
nullable,
hasteModuleName,
enumMap,
parser,
);
}
default: {
const commonType = emitCommonTypes(
hasteModuleName,
types,
typeAnnotation,
aliasMap,
enumMap,
tryParse,
cxxOnly,
nullable,
parser,
);
if (!commonType) {
throw new UnsupportedTypeAnnotationParserError(
hasteModuleName,
typeAnnotation,
parser.language(),
);
}
return commonType;
}
}
}
module.exports = {
flowTranslateTypeAnnotation: translateTypeAnnotation,
};

View File

@@ -0,0 +1,266 @@
/**
* 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 {
NamedShape,
NativeModuleAliasMap,
NativeModuleBaseTypeAnnotation,
NativeModuleEnumMap,
NativeModuleTypeAnnotation,
Nullable,
} from '../../../CodegenSchema';
import type {Parser} from '../../parser';
import type {ParserErrorCapturer, TypeDeclarationMap} from '../../utils';
const {
UnsupportedGenericParserError,
UnsupportedTypeAnnotationParserError,
} = require('../../errors');
const {
assertGenericTypeAnnotationHasExactlyOneTypeParameter,
parseObjectProperty,
unwrapNullable,
wrapNullable,
} = require('../../parsers-commons');
const {
emitArrayType,
emitCommonTypes,
emitDictionary,
emitFunction,
emitPromise,
emitRootTag,
emitUnion,
typeAliasResolution,
typeEnumResolution,
} = require('../../parsers-primitives');
function translateTypeAnnotation(
hasteModuleName: string,
/**
* TODO(T71778680): Flow-type this node.
*/
flowTypeAnnotation: $FlowFixMe,
types: TypeDeclarationMap,
aliasMap: {...NativeModuleAliasMap},
enumMap: {...NativeModuleEnumMap},
tryParse: ParserErrorCapturer,
cxxOnly: boolean,
parser: Parser,
): Nullable<NativeModuleTypeAnnotation> {
const resolveTypeAnnotationFN = parser.getResolveTypeAnnotationFN();
const {nullable, typeAnnotation, typeResolutionStatus} =
resolveTypeAnnotationFN(flowTypeAnnotation, types, parser);
switch (typeAnnotation.type) {
case 'GenericTypeAnnotation': {
switch (parser.getTypeAnnotationName(typeAnnotation)) {
case 'RootTag': {
return emitRootTag(nullable);
}
case 'Promise': {
return emitPromise(
hasteModuleName,
typeAnnotation,
parser,
nullable,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
translateTypeAnnotation,
);
}
case 'Array':
case '$ReadOnlyArray': {
return emitArrayType(
hasteModuleName,
typeAnnotation,
parser,
types,
aliasMap,
enumMap,
cxxOnly,
nullable,
translateTypeAnnotation,
);
}
case '$ReadOnly': {
assertGenericTypeAnnotationHasExactlyOneTypeParameter(
hasteModuleName,
typeAnnotation,
parser,
);
const [paramType, isParamNullable] = unwrapNullable(
translateTypeAnnotation(
hasteModuleName,
typeAnnotation.typeParameters.params[0],
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
),
);
return wrapNullable(nullable || isParamNullable, paramType);
}
default: {
const commonType = emitCommonTypes(
hasteModuleName,
types,
typeAnnotation,
aliasMap,
enumMap,
tryParse,
cxxOnly,
nullable,
parser,
);
if (!commonType) {
throw new UnsupportedGenericParserError(
hasteModuleName,
typeAnnotation,
parser,
);
}
return commonType;
}
}
}
case 'ObjectTypeAnnotation': {
// if there is any indexer, then it is a dictionary
if (typeAnnotation.indexers) {
const indexers = typeAnnotation.indexers.filter(
member => member.type === 'ObjectTypeIndexer',
);
if (indexers.length > 0) {
// check the property type to prevent developers from using unsupported types
// the return value from `translateTypeAnnotation` is unused
const propertyType = indexers[0].value;
const valueType = translateTypeAnnotation(
hasteModuleName,
propertyType,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
);
// no need to do further checking
return emitDictionary(nullable, valueType);
}
}
const objectTypeAnnotation = {
type: 'ObjectTypeAnnotation',
// $FlowFixMe[missing-type-arg]
properties: ([
...typeAnnotation.properties,
...typeAnnotation.indexers,
]: Array<$FlowFixMe>)
.map<?NamedShape<Nullable<NativeModuleBaseTypeAnnotation>>>(
property => {
return tryParse(() => {
return parseObjectProperty(
flowTypeAnnotation,
property,
hasteModuleName,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
nullable,
translateTypeAnnotation,
parser,
);
});
},
)
.filter(Boolean),
};
return typeAliasResolution(
typeResolutionStatus,
objectTypeAnnotation,
aliasMap,
nullable,
);
}
case 'FunctionTypeAnnotation': {
return emitFunction(
nullable,
hasteModuleName,
typeAnnotation,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
translateTypeAnnotation,
parser,
);
}
case 'UnionTypeAnnotation': {
return emitUnion(nullable, hasteModuleName, typeAnnotation, parser);
}
case 'StringLiteralTypeAnnotation': {
// 'a' is a special case for 'a' | 'b' but the type name is different
return wrapNullable(nullable, {
type: 'UnionTypeAnnotation',
memberType: 'StringTypeAnnotation',
});
}
case 'EnumStringBody':
case 'EnumNumberBody': {
return typeEnumResolution(
typeAnnotation,
typeResolutionStatus,
nullable,
hasteModuleName,
enumMap,
parser,
);
}
default: {
const commonType = emitCommonTypes(
hasteModuleName,
types,
typeAnnotation,
aliasMap,
enumMap,
tryParse,
cxxOnly,
nullable,
parser,
);
if (!commonType) {
throw new UnsupportedTypeAnnotationParserError(
hasteModuleName,
typeAnnotation,
parser.language(),
);
}
return commonType;
}
}
}
module.exports = {
flowTranslateTypeAnnotation: translateTypeAnnotation,
};

View File

@@ -0,0 +1,105 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
'use strict';
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
enumerableOnly &&
(symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
})),
keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread(target) {
for (var i = 1; i < arguments.length; i++) {
var source = null != arguments[i] ? arguments[i] : {};
i % 2
? ownKeys(Object(source), !0).forEach(function (key) {
_defineProperty(target, key, source[key]);
})
: Object.getOwnPropertyDescriptors
? Object.defineProperties(
target,
Object.getOwnPropertyDescriptors(source),
)
: ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(
target,
key,
Object.getOwnPropertyDescriptor(source, key),
);
});
}
return target;
}
function _defineProperty(obj, key, value) {
key = _toPropertyKey(key);
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true,
});
} else {
obj[key] = value;
}
return obj;
}
function _toPropertyKey(arg) {
var key = _toPrimitive(arg, 'string');
return typeof key === 'symbol' ? key : String(key);
}
function _toPrimitive(input, hint) {
if (typeof input !== 'object' || input === null) return input;
var prim = input[Symbol.toPrimitive];
if (prim !== undefined) {
var res = prim.call(input, hint || 'default');
if (typeof res !== 'object') return res;
throw new TypeError('@@toPrimitive must return a primitive value.');
}
return (hint === 'string' ? String : Number)(input);
}
const hermesParser = require('hermes-parser');
function parseFlowAndThrowErrors(code, options = {}) {
let ast;
try {
ast = hermesParser.parse(
code,
_objectSpread(
{
// Produce an ESTree-compliant AST
babel: false,
// Parse Flow without a pragma
flow: 'all',
},
options.filename != null
? {
sourceFilename: options.filename,
}
: {},
),
);
} catch (e) {
if (options.filename != null) {
e.message = `Syntax error in ${options.filename}: ${e.message}`;
}
throw e;
}
return ast;
}
module.exports = {
parseFlowAndThrowErrors,
};

View File

@@ -0,0 +1,41 @@
/**
* 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 {Program as ESTreeProgram} from 'hermes-estree';
const hermesParser = require('hermes-parser');
function parseFlowAndThrowErrors(
code: string,
options: $ReadOnly<{filename?: ?string}> = {},
): ESTreeProgram {
let ast;
try {
ast = hermesParser.parse(code, {
// Produce an ESTree-compliant AST
babel: false,
// Parse Flow without a pragma
flow: 'all',
...(options.filename != null ? {sourceFilename: options.filename} : {}),
});
} catch (e) {
if (options.filename != null) {
e.message = `Syntax error in ${options.filename}: ${e.message}`;
}
throw e;
}
return ast;
}
module.exports = {
parseFlowAndThrowErrors,
};

View File

@@ -0,0 +1,17 @@
/**
* 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.
*/
import type { Parser } from '../parser';
import type { SchemaType } from '../../CodegenSchema';
import type { ParserType } from '../errors';
export declare class FlowParser implements Parser {
language(): ParserType;
parseFile(filename: string): SchemaType;
parseString(contents: string, filename?: string): SchemaType;
parseModuleFixture(filename: string): SchemaType;
}

View File

@@ -0,0 +1,481 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
'use strict';
function _defineProperty(obj, key, value) {
key = _toPropertyKey(key);
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true,
});
} else {
obj[key] = value;
}
return obj;
}
function _toPropertyKey(arg) {
var key = _toPrimitive(arg, 'string');
return typeof key === 'symbol' ? key : String(key);
}
function _toPrimitive(input, hint) {
if (typeof input !== 'object' || input === null) return input;
var prim = input[Symbol.toPrimitive];
if (prim !== undefined) {
var res = prim.call(input, hint || 'default');
if (typeof res !== 'object') return res;
throw new TypeError('@@toPrimitive must return a primitive value.');
}
return (hint === 'string' ? String : Number)(input);
}
const _require = require('../errors'),
UnsupportedObjectPropertyTypeAnnotationParserError =
_require.UnsupportedObjectPropertyTypeAnnotationParserError;
const _require2 = require('../parsers-commons'),
buildModuleSchema = _require2.buildModuleSchema,
buildPropSchema = _require2.buildPropSchema,
buildSchema = _require2.buildSchema,
handleGenericTypeAnnotation = _require2.handleGenericTypeAnnotation;
const _require3 = require('../parsers-primitives'),
Visitor = _require3.Visitor;
const _require4 = require('../schema.js'),
wrapComponentSchema = _require4.wrapComponentSchema;
const _require5 = require('./components'),
buildComponentSchema = _require5.buildComponentSchema;
const _require6 = require('./components/componentsUtils'),
flattenProperties = _require6.flattenProperties,
getSchemaInfo = _require6.getSchemaInfo,
getTypeAnnotation = _require6.getTypeAnnotation;
const _require7 = require('./modules'),
flowTranslateTypeAnnotation = _require7.flowTranslateTypeAnnotation;
const _require8 = require('./parseFlowAndThrowErrors'),
parseFlowAndThrowErrors = _require8.parseFlowAndThrowErrors;
const fs = require('fs');
const invariant = require('invariant');
class FlowParser {
constructor() {
_defineProperty(
this,
'typeParameterInstantiation',
'TypeParameterInstantiation',
);
_defineProperty(this, 'typeAlias', 'TypeAlias');
_defineProperty(this, 'enumDeclaration', 'EnumDeclaration');
_defineProperty(this, 'interfaceDeclaration', 'InterfaceDeclaration');
_defineProperty(
this,
'nullLiteralTypeAnnotation',
'NullLiteralTypeAnnotation',
);
_defineProperty(
this,
'undefinedLiteralTypeAnnotation',
'VoidLiteralTypeAnnotation',
);
}
isProperty(property) {
return property.type === 'ObjectTypeProperty';
}
getKeyName(property, hasteModuleName) {
if (!this.isProperty(property)) {
throw new UnsupportedObjectPropertyTypeAnnotationParserError(
hasteModuleName,
property,
property.type,
this.language(),
);
}
return property.key.name;
}
language() {
return 'Flow';
}
getTypeAnnotationName(typeAnnotation) {
var _typeAnnotation$id;
return typeAnnotation === null || typeAnnotation === void 0
? void 0
: (_typeAnnotation$id = typeAnnotation.id) === null ||
_typeAnnotation$id === void 0
? void 0
: _typeAnnotation$id.name;
}
checkIfInvalidModule(typeArguments) {
return (
typeArguments.type !== 'TypeParameterInstantiation' ||
typeArguments.params.length !== 1 ||
typeArguments.params[0].type !== 'GenericTypeAnnotation' ||
typeArguments.params[0].id.name !== 'Spec'
);
}
remapUnionTypeAnnotationMemberNames(membersTypes) {
const remapLiteral = item => {
return item.type
.replace('NumberLiteralTypeAnnotation', 'NumberTypeAnnotation')
.replace('StringLiteralTypeAnnotation', 'StringTypeAnnotation');
};
return [...new Set(membersTypes.map(remapLiteral))];
}
parseFile(filename) {
const contents = fs.readFileSync(filename, 'utf8');
return this.parseString(contents, filename);
}
parseString(contents, filename) {
return buildSchema(
contents,
filename,
wrapComponentSchema,
buildComponentSchema,
buildModuleSchema,
Visitor,
this,
flowTranslateTypeAnnotation,
);
}
parseModuleFixture(filename) {
const contents = fs.readFileSync(filename, 'utf8');
return this.parseString(contents, 'path/NativeSampleTurboModule.js');
}
getAst(contents, filename) {
return parseFlowAndThrowErrors(contents, {
filename,
});
}
getFunctionTypeAnnotationParameters(functionTypeAnnotation) {
return functionTypeAnnotation.params;
}
getFunctionNameFromParameter(parameter) {
return parameter.name;
}
getParameterName(parameter) {
return parameter.name.name;
}
getParameterTypeAnnotation(parameter) {
return parameter.typeAnnotation;
}
getFunctionTypeAnnotationReturnType(functionTypeAnnotation) {
return functionTypeAnnotation.returnType;
}
parseEnumMembersType(typeAnnotation) {
const enumMembersType =
typeAnnotation.type === 'EnumStringBody'
? 'StringTypeAnnotation'
: typeAnnotation.type === 'EnumNumberBody'
? 'NumberTypeAnnotation'
: null;
if (!enumMembersType) {
throw new Error(
`Unknown enum type annotation type. Got: ${typeAnnotation.type}. Expected: EnumStringBody or EnumNumberBody.`,
);
}
return enumMembersType;
}
validateEnumMembersSupported(typeAnnotation, enumMembersType) {
if (!typeAnnotation.members || typeAnnotation.members.length === 0) {
// passing mixed members to flow would result in a flow error
// if the tool is launched ignoring that error, the enum would appear like not having enums
throw new Error(
'Enums should have at least one member and member values can not be mixed- they all must be either blank, number, or string values.',
);
}
typeAnnotation.members.forEach(member => {
if (
enumMembersType === 'StringTypeAnnotation' &&
(!member.init || typeof member.init.value === 'string')
) {
return;
}
if (
enumMembersType === 'NumberTypeAnnotation' &&
member.init &&
typeof member.init.value === 'number'
) {
return;
}
throw new Error(
'Enums can not be mixed- they all must be either blank, number, or string values.',
);
});
}
parseEnumMembers(typeAnnotation) {
return typeAnnotation.members.map(member => {
var _member$init$value, _member$init;
return {
name: member.id.name,
value:
(_member$init$value =
(_member$init = member.init) === null || _member$init === void 0
? void 0
: _member$init.value) !== null && _member$init$value !== void 0
? _member$init$value
: member.id.name,
};
});
}
isModuleInterface(node) {
return (
node.type === 'InterfaceDeclaration' &&
node.extends.length === 1 &&
node.extends[0].type === 'InterfaceExtends' &&
node.extends[0].id.name === 'TurboModule'
);
}
isGenericTypeAnnotation(type) {
return type === 'GenericTypeAnnotation';
}
extractAnnotatedElement(typeAnnotation, types) {
return types[typeAnnotation.typeParameters.params[0].id.name];
}
/**
* This FlowFixMe is supposed to refer to an InterfaceDeclaration or TypeAlias
* declaration type. Unfortunately, we don't have those types, because flow-parser
* generates them, and flow-parser is not type-safe. In the future, we should find
* a way to get these types from our flow parser library.
*
* TODO(T71778680): Flow type AST Nodes
*/
getTypes(ast) {
return ast.body.reduce((types, node) => {
if (
node.type === 'ExportNamedDeclaration' &&
node.exportKind === 'type'
) {
if (
node.declaration != null &&
(node.declaration.type === 'TypeAlias' ||
node.declaration.type === 'InterfaceDeclaration')
) {
types[node.declaration.id.name] = node.declaration;
}
} else if (
node.type === 'ExportNamedDeclaration' &&
node.exportKind === 'value' &&
node.declaration &&
node.declaration.type === 'EnumDeclaration'
) {
types[node.declaration.id.name] = node.declaration;
} else if (
node.type === 'TypeAlias' ||
node.type === 'InterfaceDeclaration' ||
node.type === 'EnumDeclaration'
) {
types[node.id.name] = node;
}
return types;
}, {});
}
callExpressionTypeParameters(callExpression) {
return callExpression.typeArguments || null;
}
computePartialProperties(
properties,
hasteModuleName,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
) {
return properties.map(prop => {
return {
name: prop.key.name,
optional: true,
typeAnnotation: flowTranslateTypeAnnotation(
hasteModuleName,
prop.value,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
this,
),
};
});
}
functionTypeAnnotation(propertyValueType) {
return propertyValueType === 'FunctionTypeAnnotation';
}
getTypeArgumentParamsFromDeclaration(declaration) {
return declaration.typeArguments.params;
}
/**
* This FlowFixMe is supposed to refer to typeArgumentParams and
* funcArgumentParams of generated AST.
*/
getNativeComponentType(typeArgumentParams, funcArgumentParams) {
return {
propsTypeName: typeArgumentParams[0].id.name,
componentName: funcArgumentParams[0].value,
};
}
getAnnotatedElementProperties(annotatedElement) {
return annotatedElement.right.properties;
}
bodyProperties(typeAlias) {
return typeAlias.body.properties;
}
convertKeywordToTypeAnnotation(keyword) {
return keyword;
}
argumentForProp(prop) {
return prop.argument;
}
nameForArgument(prop) {
return prop.argument.id.name;
}
isOptionalProperty(property) {
return (
property.value.type === 'NullableTypeAnnotation' || property.optional
);
}
getGetSchemaInfoFN() {
return getSchemaInfo;
}
getTypeAnnotationFromProperty(property) {
return property.value.type === 'NullableTypeAnnotation'
? property.value.typeAnnotation
: property.value;
}
getGetTypeAnnotationFN() {
return getTypeAnnotation;
}
getResolvedTypeAnnotation(typeAnnotation, types, parser) {
invariant(
typeAnnotation != null,
'resolveTypeAnnotation(): typeAnnotation cannot be null',
);
let node = typeAnnotation;
let nullable = false;
let typeResolutionStatus = {
successful: false,
};
for (;;) {
if (node.type === 'NullableTypeAnnotation') {
nullable = true;
node = node.typeAnnotation;
continue;
}
if (node.type !== 'GenericTypeAnnotation') {
break;
}
const typeAnnotationName = this.getTypeAnnotationName(node);
const resolvedTypeAnnotation = types[typeAnnotationName];
if (resolvedTypeAnnotation == null) {
break;
}
const _handleGenericTypeAnn = handleGenericTypeAnnotation(
node,
resolvedTypeAnnotation,
this,
),
typeAnnotationNode = _handleGenericTypeAnn.typeAnnotation,
status = _handleGenericTypeAnn.typeResolutionStatus;
typeResolutionStatus = status;
node = typeAnnotationNode;
}
return {
nullable: nullable,
typeAnnotation: node,
typeResolutionStatus,
};
}
getResolveTypeAnnotationFN() {
return (typeAnnotation, types, parser) =>
this.getResolvedTypeAnnotation(typeAnnotation, types, parser);
}
extendsForProp(prop, types, parser) {
const argument = this.argumentForProp(prop);
if (!argument) {
console.log('null', prop);
}
const name = parser.nameForArgument(prop);
if (types[name] != null) {
// This type is locally defined in the file
return null;
}
switch (name) {
case 'ViewProps':
return {
type: 'ReactNativeBuiltInType',
knownTypeName: 'ReactNativeCoreViewProps',
};
default: {
throw new Error(`Unable to handle prop spread: ${name}`);
}
}
}
removeKnownExtends(typeDefinition, types) {
return typeDefinition.filter(
prop =>
prop.type !== 'ObjectTypeSpreadProperty' ||
this.extendsForProp(prop, types, this) === null,
);
}
getExtendsProps(typeDefinition, types) {
return typeDefinition
.filter(prop => prop.type === 'ObjectTypeSpreadProperty')
.map(prop => this.extendsForProp(prop, types, this))
.filter(Boolean);
}
getProps(typeDefinition, types) {
const nonExtendsProps = this.removeKnownExtends(typeDefinition, types);
const props = flattenProperties(nonExtendsProps, types, this)
.map(property => buildPropSchema(property, types, this))
.filter(Boolean);
return {
props,
extendsProps: this.getExtendsProps(typeDefinition, types),
};
}
getProperties(typeName, types) {
const typeAlias = types[typeName];
try {
return typeAlias.right.typeParameters.params[0].properties;
} catch (e) {
throw new Error(
`Failed to find type definition for "${typeName}", please check that you have a valid codegen flow file`,
);
}
}
nextNodeForTypeAlias(typeAnnotation) {
return typeAnnotation.right;
}
nextNodeForEnum(typeAnnotation) {
return typeAnnotation.body;
}
genericTypeAnnotationErrorMessage(typeAnnotation) {
return `A non GenericTypeAnnotation must be a type declaration ('${this.typeAlias}') or enum ('${this.enumDeclaration}'). Instead, got the unsupported ${typeAnnotation.type}.`;
}
extractTypeFromTypeAnnotation(typeAnnotation) {
return typeAnnotation.type === 'GenericTypeAnnotation'
? typeAnnotation.id.name
: typeAnnotation.type;
}
getObjectProperties(typeAnnotation) {
return typeAnnotation.properties;
}
getLiteralValue(option) {
return option.value;
}
getPaperTopLevelNameDeprecated(typeAnnotation) {
return typeAnnotation.typeParameters.params.length > 1
? typeAnnotation.typeParameters.params[1].value
: null;
}
}
module.exports = {
FlowParser,
};

View File

@@ -0,0 +1,551 @@
/**
* 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 {
ExtendsPropsShape,
NamedShape,
NativeModuleAliasMap,
NativeModuleEnumMap,
NativeModuleEnumMembers,
NativeModuleEnumMemberType,
NativeModuleParamTypeAnnotation,
Nullable,
PropTypeAnnotation,
SchemaType,
UnionTypeAnnotationMemberType,
} from '../../CodegenSchema';
import type {ParserType} from '../errors';
import type {
GetSchemaInfoFN,
GetTypeAnnotationFN,
Parser,
ResolveTypeAnnotationFN,
} from '../parser';
import type {
ParserErrorCapturer,
PropAST,
TypeDeclarationMap,
TypeResolutionStatus,
} from '../utils';
const {
UnsupportedObjectPropertyTypeAnnotationParserError,
} = require('../errors');
const {
buildModuleSchema,
buildPropSchema,
buildSchema,
handleGenericTypeAnnotation,
} = require('../parsers-commons');
const {Visitor} = require('../parsers-primitives');
const {wrapComponentSchema} = require('../schema.js');
const {buildComponentSchema} = require('./components');
const {
flattenProperties,
getSchemaInfo,
getTypeAnnotation,
} = require('./components/componentsUtils');
const {flowTranslateTypeAnnotation} = require('./modules');
const {parseFlowAndThrowErrors} = require('./parseFlowAndThrowErrors');
const fs = require('fs');
const invariant = require('invariant');
type ExtendsForProp = null | {
type: 'ReactNativeBuiltInType',
knownTypeName: 'ReactNativeCoreViewProps',
};
class FlowParser implements Parser {
typeParameterInstantiation: string = 'TypeParameterInstantiation';
typeAlias: string = 'TypeAlias';
enumDeclaration: string = 'EnumDeclaration';
interfaceDeclaration: string = 'InterfaceDeclaration';
nullLiteralTypeAnnotation: string = 'NullLiteralTypeAnnotation';
undefinedLiteralTypeAnnotation: string = 'VoidLiteralTypeAnnotation';
isProperty(property: $FlowFixMe): boolean {
return property.type === 'ObjectTypeProperty';
}
getKeyName(property: $FlowFixMe, hasteModuleName: string): string {
if (!this.isProperty(property)) {
throw new UnsupportedObjectPropertyTypeAnnotationParserError(
hasteModuleName,
property,
property.type,
this.language(),
);
}
return property.key.name;
}
language(): ParserType {
return 'Flow';
}
getTypeAnnotationName(typeAnnotation: $FlowFixMe): string {
return typeAnnotation?.id?.name;
}
checkIfInvalidModule(typeArguments: $FlowFixMe): boolean {
return (
typeArguments.type !== 'TypeParameterInstantiation' ||
typeArguments.params.length !== 1 ||
typeArguments.params[0].type !== 'GenericTypeAnnotation' ||
typeArguments.params[0].id.name !== 'Spec'
);
}
remapUnionTypeAnnotationMemberNames(
membersTypes: $FlowFixMe[],
): UnionTypeAnnotationMemberType[] {
const remapLiteral = (item: $FlowFixMe) => {
return item.type
.replace('NumberLiteralTypeAnnotation', 'NumberTypeAnnotation')
.replace('StringLiteralTypeAnnotation', 'StringTypeAnnotation');
};
return [...new Set(membersTypes.map(remapLiteral))];
}
parseFile(filename: string): SchemaType {
const contents = fs.readFileSync(filename, 'utf8');
return this.parseString(contents, filename);
}
parseString(contents: string, filename: ?string): SchemaType {
return buildSchema(
contents,
filename,
wrapComponentSchema,
buildComponentSchema,
buildModuleSchema,
Visitor,
this,
flowTranslateTypeAnnotation,
);
}
parseModuleFixture(filename: string): SchemaType {
const contents = fs.readFileSync(filename, 'utf8');
return this.parseString(contents, 'path/NativeSampleTurboModule.js');
}
getAst(contents: string, filename?: ?string): $FlowFixMe {
return parseFlowAndThrowErrors(contents, {filename});
}
getFunctionTypeAnnotationParameters(
functionTypeAnnotation: $FlowFixMe,
): $ReadOnlyArray<$FlowFixMe> {
return functionTypeAnnotation.params;
}
getFunctionNameFromParameter(
parameter: NamedShape<Nullable<NativeModuleParamTypeAnnotation>>,
): $FlowFixMe {
return parameter.name;
}
getParameterName(parameter: $FlowFixMe): string {
return parameter.name.name;
}
getParameterTypeAnnotation(parameter: $FlowFixMe): $FlowFixMe {
return parameter.typeAnnotation;
}
getFunctionTypeAnnotationReturnType(
functionTypeAnnotation: $FlowFixMe,
): $FlowFixMe {
return functionTypeAnnotation.returnType;
}
parseEnumMembersType(typeAnnotation: $FlowFixMe): NativeModuleEnumMemberType {
const enumMembersType: ?NativeModuleEnumMemberType =
typeAnnotation.type === 'EnumStringBody'
? 'StringTypeAnnotation'
: typeAnnotation.type === 'EnumNumberBody'
? 'NumberTypeAnnotation'
: null;
if (!enumMembersType) {
throw new Error(
`Unknown enum type annotation type. Got: ${typeAnnotation.type}. Expected: EnumStringBody or EnumNumberBody.`,
);
}
return enumMembersType;
}
validateEnumMembersSupported(
typeAnnotation: $FlowFixMe,
enumMembersType: NativeModuleEnumMemberType,
): void {
if (!typeAnnotation.members || typeAnnotation.members.length === 0) {
// passing mixed members to flow would result in a flow error
// if the tool is launched ignoring that error, the enum would appear like not having enums
throw new Error(
'Enums should have at least one member and member values can not be mixed- they all must be either blank, number, or string values.',
);
}
typeAnnotation.members.forEach(member => {
if (
enumMembersType === 'StringTypeAnnotation' &&
(!member.init || typeof member.init.value === 'string')
) {
return;
}
if (
enumMembersType === 'NumberTypeAnnotation' &&
member.init &&
typeof member.init.value === 'number'
) {
return;
}
throw new Error(
'Enums can not be mixed- they all must be either blank, number, or string values.',
);
});
}
parseEnumMembers(typeAnnotation: $FlowFixMe): NativeModuleEnumMembers {
return typeAnnotation.members.map(member => ({
name: member.id.name,
value: member.init?.value ?? member.id.name,
}));
}
isModuleInterface(node: $FlowFixMe): boolean {
return (
node.type === 'InterfaceDeclaration' &&
node.extends.length === 1 &&
node.extends[0].type === 'InterfaceExtends' &&
node.extends[0].id.name === 'TurboModule'
);
}
isGenericTypeAnnotation(type: $FlowFixMe): boolean {
return type === 'GenericTypeAnnotation';
}
extractAnnotatedElement(
typeAnnotation: $FlowFixMe,
types: TypeDeclarationMap,
): $FlowFixMe {
return types[typeAnnotation.typeParameters.params[0].id.name];
}
/**
* This FlowFixMe is supposed to refer to an InterfaceDeclaration or TypeAlias
* declaration type. Unfortunately, we don't have those types, because flow-parser
* generates them, and flow-parser is not type-safe. In the future, we should find
* a way to get these types from our flow parser library.
*
* TODO(T71778680): Flow type AST Nodes
*/
getTypes(ast: $FlowFixMe): TypeDeclarationMap {
return ast.body.reduce((types, node) => {
if (
node.type === 'ExportNamedDeclaration' &&
node.exportKind === 'type'
) {
if (
node.declaration != null &&
(node.declaration.type === 'TypeAlias' ||
node.declaration.type === 'InterfaceDeclaration')
) {
types[node.declaration.id.name] = node.declaration;
}
} else if (
node.type === 'ExportNamedDeclaration' &&
node.exportKind === 'value' &&
node.declaration &&
node.declaration.type === 'EnumDeclaration'
) {
types[node.declaration.id.name] = node.declaration;
} else if (
node.type === 'TypeAlias' ||
node.type === 'InterfaceDeclaration' ||
node.type === 'EnumDeclaration'
) {
types[node.id.name] = node;
}
return types;
}, {});
}
callExpressionTypeParameters(callExpression: $FlowFixMe): $FlowFixMe | null {
return callExpression.typeArguments || null;
}
computePartialProperties(
properties: Array<$FlowFixMe>,
hasteModuleName: string,
types: TypeDeclarationMap,
aliasMap: {...NativeModuleAliasMap},
enumMap: {...NativeModuleEnumMap},
tryParse: ParserErrorCapturer,
cxxOnly: boolean,
): Array<$FlowFixMe> {
return properties.map(prop => {
return {
name: prop.key.name,
optional: true,
typeAnnotation: flowTranslateTypeAnnotation(
hasteModuleName,
prop.value,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
this,
),
};
});
}
functionTypeAnnotation(propertyValueType: string): boolean {
return propertyValueType === 'FunctionTypeAnnotation';
}
getTypeArgumentParamsFromDeclaration(declaration: $FlowFixMe): $FlowFixMe {
return declaration.typeArguments.params;
}
/**
* This FlowFixMe is supposed to refer to typeArgumentParams and
* funcArgumentParams of generated AST.
*/
getNativeComponentType(
typeArgumentParams: $FlowFixMe,
funcArgumentParams: $FlowFixMe,
): {[string]: string} {
return {
propsTypeName: typeArgumentParams[0].id.name,
componentName: funcArgumentParams[0].value,
};
}
getAnnotatedElementProperties(annotatedElement: $FlowFixMe): $FlowFixMe {
return annotatedElement.right.properties;
}
bodyProperties(typeAlias: $FlowFixMe): $ReadOnlyArray<$FlowFixMe> {
return typeAlias.body.properties;
}
convertKeywordToTypeAnnotation(keyword: string): string {
return keyword;
}
argumentForProp(prop: PropAST): $FlowFixMe {
return prop.argument;
}
nameForArgument(prop: PropAST): $FlowFixMe {
return prop.argument.id.name;
}
isOptionalProperty(property: $FlowFixMe): boolean {
return (
property.value.type === 'NullableTypeAnnotation' || property.optional
);
}
getGetSchemaInfoFN(): GetSchemaInfoFN {
return getSchemaInfo;
}
getTypeAnnotationFromProperty(property: PropAST): $FlowFixMe {
return property.value.type === 'NullableTypeAnnotation'
? property.value.typeAnnotation
: property.value;
}
getGetTypeAnnotationFN(): GetTypeAnnotationFN {
return getTypeAnnotation;
}
getResolvedTypeAnnotation(
typeAnnotation: $FlowFixMe,
types: TypeDeclarationMap,
parser: Parser,
): {
nullable: boolean,
typeAnnotation: $FlowFixMe,
typeResolutionStatus: TypeResolutionStatus,
} {
invariant(
typeAnnotation != null,
'resolveTypeAnnotation(): typeAnnotation cannot be null',
);
let node = typeAnnotation;
let nullable = false;
let typeResolutionStatus: TypeResolutionStatus = {
successful: false,
};
for (;;) {
if (node.type === 'NullableTypeAnnotation') {
nullable = true;
node = node.typeAnnotation;
continue;
}
if (node.type !== 'GenericTypeAnnotation') {
break;
}
const typeAnnotationName = this.getTypeAnnotationName(node);
const resolvedTypeAnnotation = types[typeAnnotationName];
if (resolvedTypeAnnotation == null) {
break;
}
const {typeAnnotation: typeAnnotationNode, typeResolutionStatus: status} =
handleGenericTypeAnnotation(node, resolvedTypeAnnotation, this);
typeResolutionStatus = status;
node = typeAnnotationNode;
}
return {
nullable: nullable,
typeAnnotation: node,
typeResolutionStatus,
};
}
getResolveTypeAnnotationFN(): ResolveTypeAnnotationFN {
return (typeAnnotation, types, parser) =>
this.getResolvedTypeAnnotation(typeAnnotation, types, parser);
}
extendsForProp(
prop: PropAST,
types: TypeDeclarationMap,
parser: Parser,
): ExtendsForProp {
const argument = this.argumentForProp(prop);
if (!argument) {
console.log('null', prop);
}
const name = parser.nameForArgument(prop);
if (types[name] != null) {
// This type is locally defined in the file
return null;
}
switch (name) {
case 'ViewProps':
return {
type: 'ReactNativeBuiltInType',
knownTypeName: 'ReactNativeCoreViewProps',
};
default: {
throw new Error(`Unable to handle prop spread: ${name}`);
}
}
}
removeKnownExtends(
typeDefinition: $ReadOnlyArray<PropAST>,
types: TypeDeclarationMap,
): $ReadOnlyArray<PropAST> {
return typeDefinition.filter(
prop =>
prop.type !== 'ObjectTypeSpreadProperty' ||
this.extendsForProp(prop, types, this) === null,
);
}
getExtendsProps(
typeDefinition: $ReadOnlyArray<PropAST>,
types: TypeDeclarationMap,
): $ReadOnlyArray<ExtendsPropsShape> {
return typeDefinition
.filter(prop => prop.type === 'ObjectTypeSpreadProperty')
.map(prop => this.extendsForProp(prop, types, this))
.filter(Boolean);
}
getProps(
typeDefinition: $ReadOnlyArray<PropAST>,
types: TypeDeclarationMap,
): {
props: $ReadOnlyArray<NamedShape<PropTypeAnnotation>>,
extendsProps: $ReadOnlyArray<ExtendsPropsShape>,
} {
const nonExtendsProps = this.removeKnownExtends(typeDefinition, types);
const props = flattenProperties(nonExtendsProps, types, this)
.map(property => buildPropSchema(property, types, this))
.filter(Boolean);
return {
props,
extendsProps: this.getExtendsProps(typeDefinition, types),
};
}
getProperties(typeName: string, types: TypeDeclarationMap): $FlowFixMe {
const typeAlias = types[typeName];
try {
return typeAlias.right.typeParameters.params[0].properties;
} catch (e) {
throw new Error(
`Failed to find type definition for "${typeName}", please check that you have a valid codegen flow file`,
);
}
}
nextNodeForTypeAlias(typeAnnotation: $FlowFixMe): $FlowFixMe {
return typeAnnotation.right;
}
nextNodeForEnum(typeAnnotation: $FlowFixMe): $FlowFixMe {
return typeAnnotation.body;
}
genericTypeAnnotationErrorMessage(typeAnnotation: $FlowFixMe): string {
return `A non GenericTypeAnnotation must be a type declaration ('${this.typeAlias}') or enum ('${this.enumDeclaration}'). Instead, got the unsupported ${typeAnnotation.type}.`;
}
extractTypeFromTypeAnnotation(typeAnnotation: $FlowFixMe): string {
return typeAnnotation.type === 'GenericTypeAnnotation'
? typeAnnotation.id.name
: typeAnnotation.type;
}
getObjectProperties(typeAnnotation: $FlowFixMe): $FlowFixMe {
return typeAnnotation.properties;
}
getLiteralValue(option: $FlowFixMe): $FlowFixMe {
return option.value;
}
getPaperTopLevelNameDeprecated(typeAnnotation: $FlowFixMe): $FlowFixMe {
return typeAnnotation.typeParameters.params.length > 1
? typeAnnotation.typeParameters.params[1].value
: null;
}
}
module.exports = {
FlowParser,
};

View File

@@ -0,0 +1,21 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
'use strict';
function getValueFromTypes(value, types) {
if (value.type === 'GenericTypeAnnotation' && types[value.id.name]) {
return getValueFromTypes(types[value.id.name].right, types);
}
return value;
}
module.exports = {
getValueFromTypes,
};

View File

@@ -0,0 +1,24 @@
/**
* 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 {ASTNode, TypeDeclarationMap} from '../utils';
function getValueFromTypes(value: ASTNode, types: TypeDeclarationMap): ASTNode {
if (value.type === 'GenericTypeAnnotation' && types[value.id.name]) {
return getValueFromTypes(types[value.id.name].right, types);
}
return value;
}
module.exports = {
getValueFromTypes,
};