react-native-tscodegen
Advanced tools
Comparing version 0.68.5 to 0.71.0
@@ -5,3 +5,17 @@ { | ||
{ | ||
"date": "Wed, 08 Jun 2022 20:03:27 GMT", | ||
"date": "Wed, 30 Nov 2022 15:29:30 GMT", | ||
"tag": "react-native-tscodegen_v0.71.0", | ||
"version": "0.71.0", | ||
"comments": { | ||
"minor": [ | ||
{ | ||
"comment": "Catch up react-native-codegen 0.72", | ||
"author": "53799235+ZihanChen-MSFT@users.noreply.github.com", | ||
"commit": "48000f95ea4968cd6d080bbb521f40ad5e4099a3" | ||
} | ||
] | ||
} | ||
}, | ||
{ | ||
"date": "Wed, 08 Jun 2022 20:03:29 GMT", | ||
"tag": "react-native-tscodegen_v0.68.5", | ||
@@ -8,0 +22,0 @@ "version": "0.68.5", |
# Change Log - react-native-tscodegen | ||
This log was last generated on Wed, 08 Jun 2022 20:03:27 GMT and should not be manually modified. | ||
This log was last generated on Wed, 30 Nov 2022 15:29:30 GMT and should not be manually modified. | ||
## 0.71.0 | ||
Wed, 30 Nov 2022 15:29:30 GMT | ||
### Minor changes | ||
- Catch up react-native-codegen 0.72 (53799235+ZihanChen-MSFT@users.noreply.github.com) | ||
## 0.68.5 | ||
Wed, 08 Jun 2022 20:03:27 GMT | ||
Wed, 08 Jun 2022 20:03:29 GMT | ||
@@ -8,0 +14,0 @@ ### Patches |
export declare type PlatformType = 'iOS' | 'android'; | ||
export declare type SchemaType = Readonly<{ | ||
modules: Readonly<{ | ||
export interface SchemaType { | ||
readonly modules: { | ||
[hasteModuleName: string]: ComponentSchema | NativeModuleSchema; | ||
}>; | ||
}>; | ||
export declare type DoubleTypeAnnotation = Readonly<{ | ||
type: 'DoubleTypeAnnotation'; | ||
}>; | ||
export declare type FloatTypeAnnotation = Readonly<{ | ||
type: 'FloatTypeAnnotation'; | ||
}>; | ||
export declare type BooleanTypeAnnotation = Readonly<{ | ||
type: 'BooleanTypeAnnotation'; | ||
}>; | ||
export declare type Int32TypeAnnotation = Readonly<{ | ||
type: 'Int32TypeAnnotation'; | ||
}>; | ||
export declare type StringTypeAnnotation = Readonly<{ | ||
type: 'StringTypeAnnotation'; | ||
}>; | ||
export declare type StringEnumTypeAnnotation = Readonly<{ | ||
type: 'StringEnumTypeAnnotation'; | ||
options: ReadonlyArray<string>; | ||
}>; | ||
export declare type VoidTypeAnnotation = Readonly<{ | ||
type: 'VoidTypeAnnotation'; | ||
}>; | ||
export declare type ObjectTypeAnnotation<T> = Readonly<{ | ||
type: 'ObjectTypeAnnotation'; | ||
properties: ReadonlyArray<NamedShape<T>>; | ||
}>; | ||
export declare type FunctionTypeAnnotation<P, R> = Readonly<{ | ||
type: 'FunctionTypeAnnotation'; | ||
params: ReadonlyArray<NamedShape<P>>; | ||
returnTypeAnnotation: R; | ||
}>; | ||
export declare type NamedShape<T> = Readonly<{ | ||
name: string; | ||
optional: boolean; | ||
typeAnnotation: T; | ||
}>; | ||
export declare type ComponentSchema = Readonly<{ | ||
type: 'Component'; | ||
components: Readonly<{ | ||
}; | ||
} | ||
export interface DoubleTypeAnnotation { | ||
readonly type: 'DoubleTypeAnnotation'; | ||
} | ||
export interface FloatTypeAnnotation { | ||
readonly type: 'FloatTypeAnnotation'; | ||
} | ||
export interface BooleanTypeAnnotation { | ||
readonly type: 'BooleanTypeAnnotation'; | ||
} | ||
export interface Int32TypeAnnotation { | ||
readonly type: 'Int32TypeAnnotation'; | ||
} | ||
export interface StringTypeAnnotation { | ||
readonly type: 'StringTypeAnnotation'; | ||
} | ||
export interface StringEnumTypeAnnotation { | ||
readonly type: 'StringEnumTypeAnnotation'; | ||
readonly options: readonly string[]; | ||
} | ||
export interface VoidTypeAnnotation { | ||
readonly type: 'VoidTypeAnnotation'; | ||
} | ||
export interface ObjectTypeAnnotation<T> { | ||
readonly type: 'ObjectTypeAnnotation'; | ||
readonly properties: readonly NamedShape<T>[]; | ||
} | ||
export interface FunctionTypeAnnotation<P, R> { | ||
readonly type: 'FunctionTypeAnnotation'; | ||
readonly params: readonly NamedShape<P>[]; | ||
readonly returnTypeAnnotation: R; | ||
} | ||
export interface NamedShape<T> { | ||
readonly name: string; | ||
readonly optional: boolean; | ||
readonly typeAnnotation: T; | ||
} | ||
export interface ComponentSchema { | ||
readonly type: 'Component'; | ||
readonly components: { | ||
[componentName: string]: ComponentShape; | ||
}>; | ||
}>; | ||
export declare type ComponentShape = Readonly<OptionsShape & { | ||
extendsProps: ReadonlyArray<ExtendsPropsShape>; | ||
events: ReadonlyArray<EventTypeShape>; | ||
props: ReadonlyArray<NamedShape<PropTypeAnnotation>>; | ||
commands: ReadonlyArray<NamedShape<CommandTypeAnnotation>>; | ||
}>; | ||
export declare type OptionsShape = Readonly<{ | ||
interfaceOnly?: boolean; | ||
paperComponentName?: string; | ||
excludedPlatforms?: ReadonlyArray<PlatformType>; | ||
paperComponentNameDeprecated?: string; | ||
}>; | ||
export declare type ExtendsPropsShape = Readonly<{ | ||
type: 'ReactNativeBuiltInType'; | ||
knownTypeName: 'ReactNativeCoreViewProps'; | ||
}>; | ||
export declare type EventTypeShape = Readonly<{ | ||
name: string; | ||
bubblingType: 'direct' | 'bubble'; | ||
optional: boolean; | ||
paperTopLevelNameDeprecated?: string; | ||
typeAnnotation: Readonly<{ | ||
type: 'EventTypeAnnotation'; | ||
argument?: ObjectTypeAnnotation<EventTypeAnnotation>; | ||
}>; | ||
}>; | ||
export interface ObjectTypeAnnotation_EventTypeAnnotation extends ObjectTypeAnnotation<EventTypeAnnotation> { | ||
}; | ||
} | ||
export declare type EventTypeAnnotation = BooleanTypeAnnotation | StringTypeAnnotation | DoubleTypeAnnotation | FloatTypeAnnotation | Int32TypeAnnotation | StringEnumTypeAnnotation | ObjectTypeAnnotation_EventTypeAnnotation; | ||
export interface ObjectTypeAnnotation_PropTypeAnnotation extends ObjectTypeAnnotation<PropTypeAnnotation> { | ||
export interface ComponentShape extends OptionsShape { | ||
readonly extendsProps: readonly ExtendsPropsShape[]; | ||
readonly events: readonly EventTypeShape[]; | ||
readonly props: readonly NamedShape<PropTypeAnnotation>[]; | ||
readonly commands: readonly NamedShape<CommandTypeAnnotation>[]; | ||
} | ||
export declare type PropTypeAnnotation = Readonly<{ | ||
type: 'BooleanTypeAnnotation'; | ||
default: boolean | null; | ||
}> | Readonly<{ | ||
type: 'StringTypeAnnotation'; | ||
default: string | null; | ||
}> | Readonly<{ | ||
type: 'DoubleTypeAnnotation'; | ||
default: number; | ||
}> | Readonly<{ | ||
type: 'FloatTypeAnnotation'; | ||
default: number | null; | ||
}> | Readonly<{ | ||
type: 'Int32TypeAnnotation'; | ||
default: number; | ||
}> | Readonly<{ | ||
type: 'StringEnumTypeAnnotation'; | ||
default: string; | ||
options: ReadonlyArray<string>; | ||
}> | Readonly<{ | ||
type: 'Int32EnumTypeAnnotation'; | ||
default: number; | ||
options: ReadonlyArray<number>; | ||
}> | ReservedPropTypeAnnotation | ObjectTypeAnnotation_PropTypeAnnotation | Readonly<{ | ||
type: 'ArrayTypeAnnotation'; | ||
elementType: BooleanTypeAnnotation | StringTypeAnnotation | DoubleTypeAnnotation | FloatTypeAnnotation | Int32TypeAnnotation | Readonly<{ | ||
type: 'StringEnumTypeAnnotation'; | ||
default: string; | ||
options: ReadonlyArray<string>; | ||
}> | ObjectTypeAnnotation<PropTypeAnnotation> | ReservedPropTypeAnnotation | Readonly<{ | ||
type: 'ArrayTypeAnnotation'; | ||
elementType: ObjectTypeAnnotation<PropTypeAnnotation>; | ||
}>; | ||
}>; | ||
export declare type ReservedPropTypeAnnotation = Readonly<{ | ||
type: 'ReservedPropTypeAnnotation'; | ||
name: 'ColorPrimitive' | 'ImageSourcePrimitive' | 'PointPrimitive' | 'EdgeInsetsPrimitive'; | ||
}>; | ||
export interface OptionsShape { | ||
readonly interfaceOnly?: boolean; | ||
readonly paperComponentName?: string; | ||
readonly excludedPlatforms?: readonly PlatformType[]; | ||
readonly paperComponentNameDeprecated?: string; | ||
} | ||
export interface ExtendsPropsShape { | ||
readonly type: 'ReactNativeBuiltInType'; | ||
readonly knownTypeName: 'ReactNativeCoreViewProps'; | ||
} | ||
export interface EventTypeShape { | ||
readonly name: string; | ||
readonly bubblingType: 'direct' | 'bubble'; | ||
readonly optional: boolean; | ||
readonly paperTopLevelNameDeprecated?: string; | ||
readonly typeAnnotation: { | ||
readonly type: 'EventTypeAnnotation'; | ||
readonly argument?: ObjectTypeAnnotation<EventTypeAnnotation>; | ||
}; | ||
} | ||
export declare type EventTypeAnnotation = BooleanTypeAnnotation | StringTypeAnnotation | DoubleTypeAnnotation | FloatTypeAnnotation | Int32TypeAnnotation | StringEnumTypeAnnotation | ObjectTypeAnnotation<EventTypeAnnotation>; | ||
export declare type PropTypeAnnotation = { | ||
readonly type: 'BooleanTypeAnnotation'; | ||
readonly default: boolean | null; | ||
} | { | ||
readonly type: 'StringTypeAnnotation'; | ||
readonly default: string | null; | ||
} | { | ||
readonly type: 'DoubleTypeAnnotation'; | ||
readonly default: number; | ||
} | { | ||
readonly type: 'FloatTypeAnnotation'; | ||
readonly default: number | null; | ||
} | { | ||
readonly type: 'Int32TypeAnnotation'; | ||
readonly default: number; | ||
} | { | ||
readonly type: 'StringEnumTypeAnnotation'; | ||
readonly default: string; | ||
readonly options: readonly string[]; | ||
} | { | ||
readonly type: 'Int32EnumTypeAnnotation'; | ||
readonly default: number; | ||
readonly options: readonly number[]; | ||
} | ReservedPropTypeAnnotation | ObjectTypeAnnotation<PropTypeAnnotation> | { | ||
readonly type: 'ArrayTypeAnnotation'; | ||
readonly elementType: BooleanTypeAnnotation | StringTypeAnnotation | DoubleTypeAnnotation | FloatTypeAnnotation | Int32TypeAnnotation | { | ||
readonly type: 'StringEnumTypeAnnotation'; | ||
readonly default: string; | ||
readonly options: readonly string[]; | ||
} | ObjectTypeAnnotation<PropTypeAnnotation> | ReservedPropTypeAnnotation | { | ||
readonly type: 'ArrayTypeAnnotation'; | ||
readonly elementType: ObjectTypeAnnotation<PropTypeAnnotation>; | ||
}; | ||
}; | ||
export interface ReservedPropTypeAnnotation { | ||
readonly type: 'ReservedPropTypeAnnotation'; | ||
readonly name: 'ColorPrimitive' | 'ImageSourcePrimitive' | 'PointPrimitive' | 'EdgeInsetsPrimitive' | 'ImageRequestPrimitive'; | ||
} | ||
export declare type CommandTypeAnnotation = FunctionTypeAnnotation<CommandParamTypeAnnotation, VoidTypeAnnotation>; | ||
export declare type CommandParamTypeAnnotation = ReservedTypeAnnotation | BooleanTypeAnnotation | Int32TypeAnnotation | DoubleTypeAnnotation | FloatTypeAnnotation | StringTypeAnnotation; | ||
export declare type ReservedTypeAnnotation = Readonly<{ | ||
type: 'ReservedTypeAnnotation'; | ||
name: 'RootTag'; | ||
}>; | ||
export interface ReservedTypeAnnotation { | ||
readonly type: 'ReservedTypeAnnotation'; | ||
readonly name: 'RootTag'; | ||
} | ||
export declare type Nullable<T extends NativeModuleTypeAnnotation> = NullableTypeAnnotation<T> | T; | ||
export declare type NullableTypeAnnotation<T extends NativeModuleTypeAnnotation> = Readonly<{ | ||
type: 'NullableTypeAnnotation'; | ||
typeAnnotation: T; | ||
}>; | ||
export declare type NativeModuleSchema = Readonly<{ | ||
type: 'NativeModule'; | ||
aliases: NativeModuleAliasMap; | ||
spec: NativeModuleSpec; | ||
moduleNames: ReadonlyArray<string>; | ||
excludedPlatforms?: ReadonlyArray<PlatformType>; | ||
}>; | ||
export declare type NativeModuleSpec = Readonly<{ | ||
properties: ReadonlyArray<NativeModulePropertyShape>; | ||
}>; | ||
export interface NullableTypeAnnotation<T extends NativeModuleTypeAnnotation> { | ||
readonly type: 'NullableTypeAnnotation'; | ||
readonly typeAnnotation: T; | ||
} | ||
export interface NativeModuleSchema { | ||
readonly type: 'NativeModule'; | ||
readonly aliases: NativeModuleAliasMap; | ||
readonly spec: NativeModuleSpec; | ||
readonly moduleNames: readonly string[]; | ||
readonly excludedPlatforms?: readonly PlatformType[]; | ||
} | ||
export interface NativeModuleSpec { | ||
readonly properties: readonly NativeModulePropertyShape[]; | ||
} | ||
export declare type NativeModulePropertyShape = NamedShape<Nullable<NativeModuleFunctionTypeAnnotation>>; | ||
export declare type NativeModuleAliasMap = Readonly<{ | ||
[aliasName: string]: NativeModuleObjectTypeAnnotation; | ||
}>; | ||
export interface NativeModuleFunctionTypeAnnotation extends FunctionTypeAnnotation<Nullable<NativeModuleParamTypeAnnotation>, Nullable<NativeModuleReturnTypeAnnotation>> { | ||
export interface NativeModuleAliasMap { | ||
readonly [aliasName: string]: NativeModuleObjectTypeAnnotation; | ||
} | ||
export interface NativeModuleObjectTypeAnnotation extends ObjectTypeAnnotation<Nullable<NativeModuleBaseTypeAnnotation>> { | ||
export declare type NativeModuleFunctionTypeAnnotation = FunctionTypeAnnotation<Nullable<NativeModuleParamTypeAnnotation>, Nullable<NativeModuleReturnTypeAnnotation>>; | ||
export declare type NativeModuleObjectTypeAnnotation = ObjectTypeAnnotation<Nullable<NativeModuleBaseTypeAnnotation>>; | ||
export interface NativeModuleArrayTypeAnnotation<T extends Nullable<NativeModuleBaseTypeAnnotation>> { | ||
readonly type: 'ArrayTypeAnnotation'; | ||
readonly elementType?: T; | ||
} | ||
export declare type NativeModuleArrayTypeAnnotation<T extends Nullable<NativeModuleBaseTypeAnnotation>> = Readonly<{ | ||
type: 'ArrayTypeAnnotation'; | ||
elementType?: T; | ||
}>; | ||
export declare type NativeModuleStringTypeAnnotation = Readonly<{ | ||
type: 'StringTypeAnnotation'; | ||
}>; | ||
export declare type NativeModuleNumberTypeAnnotation = Readonly<{ | ||
type: 'NumberTypeAnnotation'; | ||
}>; | ||
export declare type NativeModuleInt32TypeAnnotation = Readonly<{ | ||
type: 'Int32TypeAnnotation'; | ||
}>; | ||
export declare type NativeModuleDoubleTypeAnnotation = Readonly<{ | ||
type: 'DoubleTypeAnnotation'; | ||
}>; | ||
export declare type NativeModuleFloatTypeAnnotation = Readonly<{ | ||
type: 'FloatTypeAnnotation'; | ||
}>; | ||
export declare type NativeModuleBooleanTypeAnnotation = Readonly<{ | ||
type: 'BooleanTypeAnnotation'; | ||
}>; | ||
export declare type NativeModuleGenericObjectTypeAnnotation = Readonly<{ | ||
type: 'GenericObjectTypeAnnotation'; | ||
}>; | ||
export declare type NativeModuleTypeAliasTypeAnnotation = Readonly<{ | ||
type: 'TypeAliasTypeAnnotation'; | ||
name: string; | ||
}>; | ||
export declare type NativeModulePromiseTypeAnnotation = Readonly<{ | ||
type: 'PromiseTypeAnnotation'; | ||
}>; | ||
export interface NativeModuleArrayTypeAnnotation_Nullable_NativeModuleBaseTypeAnnotation extends NativeModuleArrayTypeAnnotation<Nullable<NativeModuleBaseTypeAnnotation>> { | ||
export interface NativeModuleStringTypeAnnotation { | ||
readonly type: 'StringTypeAnnotation'; | ||
} | ||
export declare type NativeModuleBaseTypeAnnotation = NativeModuleStringTypeAnnotation | NativeModuleNumberTypeAnnotation | NativeModuleInt32TypeAnnotation | NativeModuleDoubleTypeAnnotation | NativeModuleFloatTypeAnnotation | NativeModuleBooleanTypeAnnotation | NativeModuleGenericObjectTypeAnnotation | ReservedTypeAnnotation | NativeModuleTypeAliasTypeAnnotation | NativeModuleArrayTypeAnnotation_Nullable_NativeModuleBaseTypeAnnotation | NativeModuleObjectTypeAnnotation; | ||
export interface NativeModuleNumberTypeAnnotation { | ||
readonly type: 'NumberTypeAnnotation'; | ||
} | ||
export interface NativeModuleInt32TypeAnnotation { | ||
readonly type: 'Int32TypeAnnotation'; | ||
} | ||
export interface NativeModuleDoubleTypeAnnotation { | ||
readonly type: 'DoubleTypeAnnotation'; | ||
} | ||
export interface NativeModuleFloatTypeAnnotation { | ||
readonly type: 'FloatTypeAnnotation'; | ||
} | ||
export interface NativeModuleBooleanTypeAnnotation { | ||
readonly type: 'BooleanTypeAnnotation'; | ||
} | ||
export interface NativeModuleEnumDeclaration { | ||
readonly type: 'EnumDeclaration'; | ||
readonly memberType: 'NumberTypeAnnotation' | 'StringTypeAnnotation'; | ||
} | ||
export interface NativeModuleGenericObjectTypeAnnotation { | ||
readonly type: 'GenericObjectTypeAnnotation'; | ||
} | ||
export interface NativeModuleTypeAliasTypeAnnotation { | ||
readonly type: 'TypeAliasTypeAnnotation'; | ||
readonly name: string; | ||
} | ||
export interface NativeModulePromiseTypeAnnotation { | ||
readonly type: 'PromiseTypeAnnotation'; | ||
} | ||
export declare type UnionTypeAnnotationMemberType = 'NumberTypeAnnotation' | 'ObjectTypeAnnotation' | 'StringTypeAnnotation'; | ||
export interface NativeModuleUnionTypeAnnotation { | ||
readonly type: 'UnionTypeAnnotation'; | ||
readonly memberType: UnionTypeAnnotationMemberType; | ||
} | ||
export interface NativeModuleMixedTypeAnnotation { | ||
readonly type: 'MixedTypeAnnotation'; | ||
} | ||
export declare type NativeModuleBaseTypeAnnotation = NativeModuleStringTypeAnnotation | NativeModuleNumberTypeAnnotation | NativeModuleInt32TypeAnnotation | NativeModuleDoubleTypeAnnotation | NativeModuleFloatTypeAnnotation | NativeModuleBooleanTypeAnnotation | NativeModuleEnumDeclaration | NativeModuleGenericObjectTypeAnnotation | ReservedTypeAnnotation | NativeModuleTypeAliasTypeAnnotation | NativeModuleArrayTypeAnnotation<Nullable<NativeModuleBaseTypeAnnotation>> | NativeModuleObjectTypeAnnotation | NativeModuleUnionTypeAnnotation | NativeModuleMixedTypeAnnotation; | ||
export declare type NativeModuleParamTypeAnnotation = NativeModuleBaseTypeAnnotation | NativeModuleParamOnlyTypeAnnotation; | ||
@@ -183,0 +187,0 @@ export declare type NativeModuleReturnTypeAnnotation = NativeModuleBaseTypeAnnotation | NativeModuleReturnOnlyTypeAnnotation; |
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT license. | ||
// Automatically generated from react-native/packages/react-native-codegen/src/CodegenSchema.js | ||
// Targeting react-native 0.66-stable | ||
// Targeting react-native 1000.0.0 | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
//# sourceMappingURL=CodegenSchema.js.map |
@@ -14,3 +14,10 @@ import { SchemaType } from './CodegenSchema'; | ||
declare type GeneratePrototype = (options: Options, config: Config) => boolean; | ||
interface ParserModule { | ||
parseFile(filename: string): SchemaType; | ||
parseModuleFixture(filename: string): SchemaType; | ||
parseString(contents: string, filename: string): SchemaType; | ||
} | ||
export declare const generate: GeneratePrototype; | ||
export declare const flowParser: ParserModule; | ||
export declare const typeScriptParser: ParserModule; | ||
export {}; |
@@ -5,4 +5,6 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.generate = void 0; | ||
exports.typeScriptParser = exports.flowParser = exports.generate = void 0; | ||
exports.generate = (require('./rncodegen/src/generators/RNCodegen.js').generate); | ||
exports.flowParser = (require('./rncodegen/src/parsers/flow/index.js')); | ||
exports.typeScriptParser = (require('./rncodegen/src/parsers/typescript/index.js')); | ||
//# sourceMappingURL=ExportRNCodegen.js.map |
@@ -1,5 +0,5 @@ | ||
import * as cs from './CodegenSchema'; | ||
export declare function typeScriptToCodeSchema(fileName: string, moduleName: string, targetName?: string): cs.SchemaType; | ||
export * from './CodegenSchema'; | ||
import * as rncodegen from './ExportRNCodegen'; | ||
export import flowParser = rncodegen.flowParser; | ||
export import typeScriptParser = rncodegen.typeScriptParser; | ||
export declare namespace generator { | ||
@@ -6,0 +6,0 @@ export import generate = rncodegen.generate; |
128
lib/index.js
@@ -6,3 +6,7 @@ "use strict"; | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -16,123 +20,7 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.generator = exports.typeScriptToCodeSchema = void 0; | ||
var path = require("path"); | ||
var ts = require("typescript"); | ||
var ComponentParser_1 = require("./ComponentParser"); | ||
var ep = require("./ExportParser"); | ||
var NativeModuleParser_1 = require("./NativeModuleParser"); | ||
var TypeChecker_1 = require("./TypeChecker"); | ||
function messageChainToString(chain, indent) { | ||
var message = ''; | ||
message += "" + indent + chain.messageText; | ||
if (chain.next !== undefined) { | ||
message += " {"; | ||
var newIndent = indent + " "; | ||
for (var _i = 0, _a = chain.next; _i < _a.length; _i++) { | ||
var subChain = _a[_i]; | ||
message += "\r\n" + messageChainToString(subChain, newIndent); | ||
} | ||
message += indent + "\r\n}"; | ||
} | ||
return message; | ||
} | ||
function errorToString(error) { | ||
var message = ts.DiagnosticCategory[error.category] + ":"; | ||
if (error.source !== undefined) { | ||
message += "\r\n source : " + error.source; | ||
} | ||
if (error.file !== undefined) { | ||
message += "\r\n file : " + error.file.fileName; | ||
} | ||
if (typeof error.messageText === 'string') { | ||
message += "\r\n message : " + error.messageText; | ||
} | ||
else { | ||
message += "\r\n message {"; | ||
message += "\r\n" + messageChainToString(error.messageText, ' '); | ||
message += "\r\n}"; | ||
} | ||
return message; | ||
} | ||
function typeScriptToCodeSchema(fileName, moduleName, targetName) { | ||
var program = ts.createProgram([fileName], { | ||
strictNullChecks: true, | ||
skipLibCheck: true | ||
}); | ||
var errors = ts.getPreEmitDiagnostics(program).filter(function (value) { return value.category === ts.DiagnosticCategory.Error; }); | ||
if (errors.length > 0) { | ||
var errorMessage = 'Please ensure that the input TypeScript source file compiles.'; | ||
for (var _i = 0, errors_1 = errors; _i < errors_1.length; _i++) { | ||
var error = errors_1[_i]; | ||
errorMessage += "\r\n" + errorToString(error); | ||
} | ||
throw new Error(errorMessage); | ||
} | ||
var nativeModuleInfos = []; | ||
var componentInfos = []; | ||
var commandInfos = []; | ||
program.getSourceFiles().forEach(function (sourceFile) { | ||
if (path.basename(fileName) === path.basename(sourceFile.fileName)) { | ||
sourceFile.statements.forEach(function (node) { | ||
var currentNativeModuleInfo = ep.tryParseExportNativeModule(program, sourceFile, node); | ||
var currentComponentInfo = ep.tryParseExportComponent(program, sourceFile, node); | ||
var currentCommandInfo = ep.tryParseExportCommand(program, sourceFile, node); | ||
if (currentNativeModuleInfo !== undefined) { | ||
nativeModuleInfos.push(currentNativeModuleInfo); | ||
} | ||
if (currentComponentInfo !== undefined) { | ||
componentInfos.push(currentComponentInfo); | ||
} | ||
if (currentCommandInfo !== undefined) { | ||
commandInfos.push(currentCommandInfo); | ||
} | ||
}); | ||
} | ||
}); | ||
if (nativeModuleInfos.length + componentInfos.length === 0) { | ||
throw new Error('Cannot find any component or native module.'); | ||
} | ||
if (nativeModuleInfos.length + componentInfos.length > 1) { | ||
throw new Error('A TypeScript source file should only container either one component or one native module.'); | ||
} | ||
if (nativeModuleInfos.length === 1) { | ||
if (commandInfos.length > 0) { | ||
throw new Error('Command list should not be exported in a TypeScript source file that exports a native module.'); | ||
} | ||
// find out all type aliases in this file | ||
var aliases_1 = { aliases: {} }; | ||
var knownAliases_1 = []; | ||
program.getSourceFiles().forEach(function (sourceFile) { | ||
if (path.basename(fileName) === path.basename(sourceFile.fileName)) { | ||
sourceFile.statements.forEach(function (node) { | ||
if (ts.isTypeAliasDeclaration(node)) { | ||
if (node.typeParameters === undefined || node.typeParameters.length === 0) { | ||
var rnRawType = TypeChecker_1.typeToRNRawType(node.type, sourceFile, { allowObject: true, knownAliases: knownAliases_1 }); | ||
if (rnRawType.kind === 'Object') { | ||
var aliasName = node.name.text; | ||
aliases_1.aliases[aliasName] = rnRawType; | ||
knownAliases_1.push(aliasName); | ||
} | ||
} | ||
} | ||
}); | ||
} | ||
}); | ||
var info = nativeModuleInfos[0]; | ||
var result = { modules: {} }; | ||
result.modules[moduleName] = NativeModuleParser_1.processNativeModule(info, aliases_1); | ||
return result; | ||
} | ||
else { | ||
if (commandInfos.length > 1) { | ||
throw new Error('A TypeScript source file should not export more than one command list.'); | ||
} | ||
var info = componentInfos[0]; | ||
var result = { modules: {} }; | ||
result.modules[info.name] = ComponentParser_1.processComponent(info, commandInfos[0]); | ||
return result; | ||
} | ||
} | ||
exports.typeScriptToCodeSchema = typeScriptToCodeSchema; | ||
exports.generator = exports.typeScriptParser = exports.flowParser = void 0; | ||
__exportStar(require("./CodegenSchema"), exports); | ||
var rncodegen = require("./ExportRNCodegen"); | ||
exports.flowParser = rncodegen.flowParser; | ||
exports.typeScriptParser = rncodegen.typeScriptParser; | ||
var generator; | ||
@@ -139,0 +27,0 @@ (function (generator) { |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -22,17 +22,27 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (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 = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
const babel = require('@babel/core'); | ||
const chalk = require('chalk'); | ||
const fs = require('fs'); | ||
const glob = require('glob'); | ||
const micromatch = require('micromatch'); | ||
const mkdirp = require('mkdirp'); | ||
const path = require('path'); | ||
const prettier = require('prettier'); | ||
const prettierConfig = JSON.parse( | ||
fs.readFileSync(path.resolve(__dirname, '..', '.prettierrc'), 'utf8'), | ||
); | ||
const prettierConfig = JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', '.prettierrc'), 'utf8')); | ||
const SRC_DIR = 'src'; | ||
@@ -48,9 +58,8 @@ const BUILD_DIR = 'lib'; | ||
let lastString = strs[strs.length - 1]; | ||
if (lastString.length < WIDTH) { | ||
lastString += Array(WIDTH - lastString.length).join(chalk.dim('.')); | ||
} | ||
return strs | ||
.slice(0, -1) | ||
.concat(lastString) | ||
.join('\n'); | ||
return strs.slice(0, -1).concat(lastString).join('\n'); | ||
}; | ||
@@ -67,43 +76,21 @@ | ||
const destPath = getBuildPath(file, BUILD_DIR); | ||
mkdirp.sync(path.dirname(destPath)); | ||
mkdirp.sync(path.dirname(destPath)); | ||
if (micromatch.isMatch(file, IGNORE_PATTERN)) { | ||
silent || | ||
process.stdout.write( | ||
chalk.dim(' \u2022 ') + | ||
path.relative(PACKAGE_DIR, file) + | ||
' (ignore)\n', | ||
); | ||
silent || process.stdout.write(chalk.dim(' \u2022 ') + path.relative(PACKAGE_DIR, file) + ' (ignore)\n'); | ||
} else if (!micromatch.isMatch(file, JS_FILES_PATTERN)) { | ||
fs.createReadStream(file).pipe(fs.createWriteStream(destPath)); | ||
silent || | ||
process.stdout.write( | ||
chalk.red(' \u2022 ') + | ||
path.relative(PACKAGE_DIR, file) + | ||
chalk.red(' \u21D2 ') + | ||
path.relative(PACKAGE_DIR, destPath) + | ||
' (copy)' + | ||
'\n', | ||
); | ||
silent || process.stdout.write(chalk.red(' \u2022 ') + path.relative(PACKAGE_DIR, file) + chalk.red(' \u21D2 ') + path.relative(PACKAGE_DIR, destPath) + ' (copy)' + '\n'); | ||
} else { | ||
const transformed = prettier.format( | ||
babel.transformFileSync(file, {}).code, | ||
{ | ||
...prettierConfig, | ||
parser: 'babel', | ||
}, | ||
); | ||
const transformed = prettier.format(babel.transformFileSync(file, {}).code, _objectSpread({}, prettierConfig, { | ||
parser: 'babel' | ||
})); | ||
fs.writeFileSync(destPath, transformed); | ||
const source = fs.readFileSync(file).toString('utf-8'); | ||
if (/\ /.test(source)) { | ||
if (/ /.test(source)) { | ||
fs.createReadStream(file).pipe(fs.createWriteStream(destPath + '.flow')); | ||
} | ||
silent || | ||
process.stdout.write( | ||
chalk.green(' \u2022 ') + | ||
path.relative(PACKAGE_DIR, file) + | ||
chalk.green(' \u21D2 ') + | ||
path.relative(PACKAGE_DIR, destPath) + | ||
'\n', | ||
); | ||
silent || process.stdout.write(chalk.green(' \u2022 ') + path.relative(PACKAGE_DIR, file) + chalk.green(' \u21D2 ') + path.relative(PACKAGE_DIR, destPath) + '\n'); | ||
} | ||
@@ -114,8 +101,7 @@ } | ||
const pattern = path.resolve(srcDir, '**/*'); | ||
const files = glob.sync(pattern, {nodir: true}); | ||
const files = glob.sync(pattern, { | ||
nodir: true | ||
}); | ||
process.stdout.write(fixedWidth(`${path.basename(PACKAGE_DIR)}\n`)); | ||
files.forEach(file => buildFile(file, !process.argv.includes('--verbose'))); | ||
process.stdout.write(`[ ${chalk.green('OK')} ]\n`); | ||
process.stdout.write(`[ ${chalk.green('OK')} ]\n`); |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,319 +10,9 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
/** | ||
* Component Type Annotations | ||
*/ | ||
/** | ||
* NativeModule Types | ||
*/ | ||
*/ |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,7 +10,5 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
function upperCaseFirst(inString ) { | ||
function upperCaseFirst(inString) { | ||
if (inString.length === 0) { | ||
@@ -23,34 +21,29 @@ return inString; | ||
function toSafeCppString(input ) { | ||
return input | ||
.split('-') | ||
.map(upperCaseFirst) | ||
.join(''); | ||
function toSafeCppString(input) { | ||
return input.split('-').map(upperCaseFirst).join(''); | ||
} | ||
function toIntEnumValueName(propName , value ) { | ||
function toIntEnumValueName(propName, value) { | ||
return `${toSafeCppString(propName)}${value}`; | ||
} | ||
function getCppTypeForAnnotation( | ||
type | ||
, | ||
) { | ||
function getCppTypeForAnnotation(type) { | ||
switch (type) { | ||
case 'BooleanTypeAnnotation': | ||
return 'bool'; | ||
case 'StringTypeAnnotation': | ||
return 'std::string'; | ||
case 'Int32TypeAnnotation': | ||
return 'int'; | ||
case 'DoubleTypeAnnotation': | ||
return 'double'; | ||
case 'FloatTypeAnnotation': | ||
return 'Float'; | ||
default: | ||
(type ); | ||
type; | ||
throw new Error(`Received invalid typeAnnotation ${type}`); | ||
@@ -60,6 +53,4 @@ } | ||
function getImports( | ||
properties , | ||
) { | ||
const imports = new Set(); | ||
function getImports(properties) { | ||
const imports = new Set(); | ||
@@ -70,11 +61,18 @@ function addImportsForNativeName(name) { | ||
return; | ||
case 'PointPrimitive': | ||
return; | ||
case 'EdgeInsetsPrimitive': | ||
return; | ||
case 'ImageRequestPrimitive': | ||
return; | ||
case 'ImageSourcePrimitive': | ||
imports.add('#include <react/renderer/components/image/conversions.h>'); | ||
return; | ||
default: | ||
(name ); | ||
name; | ||
throw new Error(`Invalid name, got ${name}`); | ||
@@ -91,6 +89,3 @@ } | ||
if ( | ||
typeAnnotation.type === 'ArrayTypeAnnotation' && | ||
typeAnnotation.elementType.type === 'ReservedPropTypeAnnotation' | ||
) { | ||
if (typeAnnotation.type === 'ArrayTypeAnnotation' && typeAnnotation.elementType.type === 'ReservedPropTypeAnnotation') { | ||
addImportsForNativeName(typeAnnotation.elementType.name); | ||
@@ -100,12 +95,11 @@ } | ||
if (typeAnnotation.type === 'ObjectTypeAnnotation') { | ||
const objectImports = getImports(typeAnnotation.properties); | ||
// $FlowFixMe[method-unbinding] added when improving typing for this parameters | ||
const objectImports = getImports(typeAnnotation.properties); // $FlowFixMe[method-unbinding] added when improving typing for this parameters | ||
objectImports.forEach(imports.add, imports); | ||
} | ||
}); | ||
return imports; | ||
} | ||
function generateEventStructName(parts = []) { | ||
function generateEventStructName(parts = []) { | ||
const additional = parts.map(toSafeCppString).join(''); | ||
@@ -115,6 +109,3 @@ return `${additional}`; | ||
function generateStructName( | ||
componentName , | ||
parts = [], | ||
) { | ||
function generateStructName(componentName, parts = []) { | ||
const additional = parts.map(toSafeCppString).join(''); | ||
@@ -124,3 +115,3 @@ return `${componentName}${additional}Struct`; | ||
function getEnumName(componentName , propName ) { | ||
function getEnumName(componentName, propName) { | ||
const uppercasedPropName = toSafeCppString(propName); | ||
@@ -130,11 +121,9 @@ return `${componentName}${uppercasedPropName}`; | ||
function getEnumMaskName(enumName ) { | ||
function getEnumMaskName(enumName) { | ||
return `${enumName}Mask`; | ||
} | ||
function convertDefaultTypeToString( | ||
componentName , | ||
prop , | ||
) { | ||
function convertDefaultTypeToString(componentName, prop) { | ||
const typeAnnotation = prop.typeAnnotation; | ||
switch (typeAnnotation.type) { | ||
@@ -145,3 +134,5 @@ case 'BooleanTypeAnnotation': | ||
} | ||
return String(typeAnnotation.default); | ||
case 'StringTypeAnnotation': | ||
@@ -151,18 +142,21 @@ if (typeAnnotation.default == null) { | ||
} | ||
return `"${typeAnnotation.default}"`; | ||
case 'Int32TypeAnnotation': | ||
return String(typeAnnotation.default); | ||
case 'DoubleTypeAnnotation': | ||
const defaultDoubleVal = typeAnnotation.default; | ||
return parseInt(defaultDoubleVal, 10) === defaultDoubleVal | ||
? typeAnnotation.default.toFixed(1) | ||
: String(typeAnnotation.default); | ||
return parseInt(defaultDoubleVal, 10) === defaultDoubleVal ? typeAnnotation.default.toFixed(1) : String(typeAnnotation.default); | ||
case 'FloatTypeAnnotation': | ||
const defaultFloatVal = typeAnnotation.default; | ||
if (defaultFloatVal == null) { | ||
return ''; | ||
} | ||
return parseInt(defaultFloatVal, 10) === defaultFloatVal | ||
? defaultFloatVal.toFixed(1) | ||
: String(typeAnnotation.default); | ||
return parseInt(defaultFloatVal, 10) === defaultFloatVal ? defaultFloatVal.toFixed(1) : String(typeAnnotation.default); | ||
case 'ReservedPropTypeAnnotation': | ||
@@ -172,47 +166,53 @@ switch (typeAnnotation.name) { | ||
return ''; | ||
case 'ImageSourcePrimitive': | ||
return ''; | ||
case 'ImageRequestPrimitive': | ||
return ''; | ||
case 'PointPrimitive': | ||
return ''; | ||
case 'EdgeInsetsPrimitive': | ||
return ''; | ||
default: | ||
(typeAnnotation.name ); | ||
throw new Error( | ||
`Unsupported type annotation: ${typeAnnotation.name}`, | ||
); | ||
typeAnnotation.name; | ||
throw new Error(`Unsupported type annotation: ${typeAnnotation.name}`); | ||
} | ||
case 'ArrayTypeAnnotation': { | ||
const elementType = typeAnnotation.elementType; | ||
switch (elementType.type) { | ||
case 'StringEnumTypeAnnotation': | ||
if (elementType.default == null) { | ||
throw new Error( | ||
'A default is required for array StringEnumTypeAnnotation', | ||
); | ||
} | ||
const enumName = getEnumName(componentName, prop.name); | ||
const enumMaskName = getEnumMaskName(enumName); | ||
const defaultValue = `${enumName}::${toSafeCppString( | ||
elementType.default, | ||
)}`; | ||
return `static_cast<${enumMaskName}>(${defaultValue})`; | ||
default: | ||
return ''; | ||
case 'ArrayTypeAnnotation': | ||
{ | ||
const elementType = typeAnnotation.elementType; | ||
switch (elementType.type) { | ||
case 'StringEnumTypeAnnotation': | ||
if (elementType.default == null) { | ||
throw new Error('A default is required for array StringEnumTypeAnnotation'); | ||
} | ||
const enumName = getEnumName(componentName, prop.name); | ||
const enumMaskName = getEnumMaskName(enumName); | ||
const defaultValue = `${enumName}::${toSafeCppString(elementType.default)}`; | ||
return `static_cast<${enumMaskName}>(${defaultValue})`; | ||
default: | ||
return ''; | ||
} | ||
} | ||
} | ||
case 'ObjectTypeAnnotation': { | ||
return ''; | ||
} | ||
case 'ObjectTypeAnnotation': | ||
{ | ||
return ''; | ||
} | ||
case 'StringEnumTypeAnnotation': | ||
return `${getEnumName(componentName, prop.name)}::${toSafeCppString( | ||
typeAnnotation.default, | ||
)}`; | ||
return `${getEnumName(componentName, prop.name)}::${toSafeCppString(typeAnnotation.default)}`; | ||
case 'Int32EnumTypeAnnotation': | ||
return `${getEnumName(componentName, prop.name)}::${toIntEnumValueName( | ||
prop.name, | ||
typeAnnotation.default, | ||
)}`; | ||
return `${getEnumName(componentName, prop.name)}::${toIntEnumValueName(prop.name, typeAnnotation.default)}`; | ||
default: | ||
(typeAnnotation ); | ||
typeAnnotation; | ||
throw new Error(`Unsupported type annotation: ${typeAnnotation.type}`); | ||
@@ -231,3 +231,3 @@ } | ||
generateStructName, | ||
generateEventStructName, | ||
}; | ||
generateEventStructName | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,16 +10,13 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; // File path -> contents | ||
'use strict'; | ||
// File path -> contents | ||
const template = ` | ||
const FileTemplate = ({ | ||
componentDescriptors, | ||
libraryName | ||
}) => ` | ||
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* Do not edit this file as changes may cause incorrect behavior and will be lost | ||
* once the code is regenerated. | ||
* | ||
@@ -31,3 +28,3 @@ * ${'@'}generated by codegen project: GenerateComponentDescriptorH.js | ||
#include <react/renderer/components/::_LIBRARY_::/ShadowNodes.h> | ||
#include <react/renderer/components/${libraryName}/ShadowNodes.h> | ||
#include <react/renderer/core/ConcreteComponentDescriptor.h> | ||
@@ -38,3 +35,3 @@ | ||
::_COMPONENT_DESCRIPTORS_:: | ||
${componentDescriptors} | ||
@@ -45,46 +42,41 @@ } // namespace react | ||
const componentTemplate = ` | ||
using ::_CLASSNAME_::ComponentDescriptor = ConcreteComponentDescriptor<::_CLASSNAME_::ShadowNode>; | ||
const ComponentTemplate = ({ | ||
className | ||
}) => ` | ||
using ${className}ComponentDescriptor = ConcreteComponentDescriptor<${className}ShadowNode>; | ||
`.trim(); | ||
module.exports = { | ||
generate( | ||
libraryName , | ||
schema , | ||
packageName , | ||
assumeNonnull = false, | ||
) { | ||
generate(libraryName, schema, packageName, assumeNonnull = false) { | ||
const fileName = 'ComponentDescriptors.h'; | ||
const componentDescriptors = Object.keys(schema.modules).map(moduleName => { | ||
const module = schema.modules[moduleName]; | ||
const componentDescriptors = Object.keys(schema.modules) | ||
.map(moduleName => { | ||
const module = schema.modules[moduleName]; | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
const {components} = module; | ||
// No components in this module | ||
if (components == null) { | ||
return null; | ||
} | ||
const components = module.components; // No components in this module | ||
return Object.keys(components) | ||
.map(componentName => { | ||
if (components[componentName].interfaceOnly === true) { | ||
return; | ||
} | ||
return componentTemplate.replace(/::_CLASSNAME_::/g, componentName); | ||
}) | ||
.join('\n'); | ||
}) | ||
.filter(Boolean) | ||
.join('\n'); | ||
if (components == null) { | ||
return null; | ||
} | ||
const replacedTemplate = template | ||
.replace(/::_COMPONENT_DESCRIPTORS_::/g, componentDescriptors) | ||
.replace('::_LIBRARY_::', libraryName); | ||
return Object.keys(components).map(componentName => { | ||
if (components[componentName].interfaceOnly === true) { | ||
return; | ||
} | ||
return ComponentTemplate({ | ||
className: componentName | ||
}); | ||
}).join('\n'); | ||
}).filter(Boolean).join('\n'); | ||
const replacedTemplate = FileTemplate({ | ||
componentDescriptors, | ||
libraryName | ||
}); | ||
return new Map([[fileName, replacedTemplate]]); | ||
}, | ||
}; | ||
} | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,21 +10,12 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
function getOrdinalNumber(num ) { | ||
function getOrdinalNumber(num) { | ||
switch (num) { | ||
case 1: | ||
return '1st'; | ||
case 2: | ||
return '2nd'; | ||
case 3: | ||
@@ -41,23 +32,39 @@ return '3rd'; | ||
const protocolTemplate = ` | ||
@protocol RCT::_COMPONENT_NAME_::ViewProtocol <NSObject> | ||
::_METHODS_:: | ||
const ProtocolTemplate = ({ | ||
componentName, | ||
methods | ||
}) => ` | ||
@protocol RCT${componentName}ViewProtocol <NSObject> | ||
${methods} | ||
@end | ||
`.trim(); | ||
const commandHandlerIfCaseConvertArgTemplate = ` | ||
NSObject *arg::_ARG_NUMBER_:: = args[::_ARG_NUMBER_::]; | ||
const CommandHandlerIfCaseConvertArgTemplate = ({ | ||
componentName, | ||
expectedKind, | ||
argNumber, | ||
argNumberString, | ||
expectedKindString, | ||
argConversion | ||
}) => ` | ||
NSObject *arg${argNumber} = args[${argNumber}]; | ||
#if RCT_DEBUG | ||
if (!RCTValidateTypeOfViewCommandArgument(arg::_ARG_NUMBER_::, ::_EXPECTED_KIND_::, @"::_EXPECTED_KIND_STRING_::", @"::_COMPONENT_NAME_::", commandName, @"::_ARG_NUMBER_STR_::")) { | ||
if (!RCTValidateTypeOfViewCommandArgument(arg${argNumber}, ${expectedKind}, @"${expectedKindString}", @"${componentName}", commandName, @"${argNumberString}")) { | ||
return; | ||
} | ||
#endif | ||
::_ARG_CONVERSION_:: | ||
${argConversion} | ||
`.trim(); | ||
const commandHandlerIfCaseTemplate = ` | ||
if ([commandName isEqualToString:@"::_COMMAND_NAME_::"]) { | ||
const CommandHandlerIfCaseTemplate = ({ | ||
componentName, | ||
commandName, | ||
numArgs, | ||
convertArgs, | ||
commandCall | ||
}) => ` | ||
if ([commandName isEqualToString:@"${commandName}"]) { | ||
#if RCT_DEBUG | ||
if ([args count] != ::_NUM_ARGS_::) { | ||
RCTLogError(@"%@ command %@ received %d arguments, expected %d.", @"::_COMPONENT_NAME_::", commandName, (int)[args count], ::_NUM_ARGS_::); | ||
if ([args count] != ${numArgs}) { | ||
RCTLogError(@"%@ command %@ received %d arguments, expected %d.", @"${componentName}", commandName, (int)[args count], ${numArgs}); | ||
return; | ||
@@ -67,5 +74,5 @@ } | ||
::_CONVERT_ARGS_:: | ||
${convertArgs} | ||
::_COMMAND_CALL_:: | ||
${commandCall} | ||
return; | ||
@@ -75,12 +82,15 @@ } | ||
const commandHandlerTemplate = ` | ||
RCT_EXTERN inline void RCT::_COMPONENT_NAME_::HandleCommand( | ||
id<RCT::_COMPONENT_NAME_::ViewProtocol> componentView, | ||
const CommandHandlerTemplate = ({ | ||
componentName, | ||
ifCases | ||
}) => ` | ||
RCT_EXTERN inline void RCT${componentName}HandleCommand( | ||
id<RCT${componentName}ViewProtocol> componentView, | ||
NSString const *commandName, | ||
NSArray const *args) | ||
{ | ||
::_IF_CASES_:: | ||
${ifCases} | ||
#if RCT_DEBUG | ||
RCTLogError(@"%@ received command %@, which is not a supported command.", @"::_COMPONENT_NAME_::", commandName); | ||
RCTLogError(@"%@ received command %@, which is not a supported command.", @"${componentName}", commandName); | ||
#endif | ||
@@ -90,8 +100,10 @@ } | ||
const template = ` | ||
const FileTemplate = ({ | ||
componentContent | ||
}) => ` | ||
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* Do not edit this file as changes may cause incorrect behavior and will be lost | ||
* once the code is regenerated. | ||
* | ||
@@ -107,3 +119,3 @@ * ${'@'}generated by codegen project: GenerateComponentHObjCpp.js | ||
::_COMPONENT_CONTENT_:: | ||
${componentContent} | ||
@@ -113,7 +125,5 @@ NS_ASSUME_NONNULL_END | ||
function getObjCParamType(param) { | ||
const typeAnnotation = param.typeAnnotation; | ||
function getObjCParamType(param ) { | ||
const {typeAnnotation} = param; | ||
switch (typeAnnotation.type) { | ||
@@ -124,18 +134,25 @@ case 'ReservedTypeAnnotation': | ||
return 'double'; | ||
default: | ||
(typeAnnotation.name ); | ||
typeAnnotation.name; | ||
throw new Error(`Receieved invalid type: ${typeAnnotation.name}`); | ||
} | ||
case 'BooleanTypeAnnotation': | ||
return 'BOOL'; | ||
case 'DoubleTypeAnnotation': | ||
return 'double'; | ||
case 'FloatTypeAnnotation': | ||
return 'float'; | ||
case 'Int32TypeAnnotation': | ||
return 'NSInteger'; | ||
case 'StringTypeAnnotation': | ||
return 'NSString *'; | ||
default: | ||
(typeAnnotation.type ); | ||
typeAnnotation.type; | ||
throw new Error('Received invalid param type annotation'); | ||
@@ -145,4 +162,4 @@ } | ||
function getObjCExpectedKindParamType(param ) { | ||
const {typeAnnotation} = param; | ||
function getObjCExpectedKindParamType(param) { | ||
const typeAnnotation = param.typeAnnotation; | ||
@@ -154,18 +171,25 @@ switch (typeAnnotation.type) { | ||
return '[NSNumber class]'; | ||
default: | ||
(typeAnnotation.name ); | ||
typeAnnotation.name; | ||
throw new Error(`Receieved invalid type: ${typeAnnotation.name}`); | ||
} | ||
case 'BooleanTypeAnnotation': | ||
return '[NSNumber class]'; | ||
case 'DoubleTypeAnnotation': | ||
return '[NSNumber class]'; | ||
case 'FloatTypeAnnotation': | ||
return '[NSNumber class]'; | ||
case 'Int32TypeAnnotation': | ||
return '[NSNumber class]'; | ||
case 'StringTypeAnnotation': | ||
return '[NSString class]'; | ||
default: | ||
(typeAnnotation.type ); | ||
typeAnnotation.type; | ||
throw new Error('Received invalid param type annotation'); | ||
@@ -175,4 +199,4 @@ } | ||
function getReadableExpectedKindParamType(param ) { | ||
const {typeAnnotation} = param; | ||
function getReadableExpectedKindParamType(param) { | ||
const typeAnnotation = param.typeAnnotation; | ||
@@ -184,18 +208,25 @@ switch (typeAnnotation.type) { | ||
return 'double'; | ||
default: | ||
(typeAnnotation.name ); | ||
typeAnnotation.name; | ||
throw new Error(`Receieved invalid type: ${typeAnnotation.name}`); | ||
} | ||
case 'BooleanTypeAnnotation': | ||
return 'boolean'; | ||
case 'DoubleTypeAnnotation': | ||
return 'double'; | ||
case 'FloatTypeAnnotation': | ||
return 'float'; | ||
case 'Int32TypeAnnotation': | ||
return 'number'; | ||
case 'StringTypeAnnotation': | ||
return 'string'; | ||
default: | ||
(typeAnnotation.type ); | ||
typeAnnotation.type; | ||
throw new Error('Received invalid param type annotation'); | ||
@@ -205,7 +236,4 @@ } | ||
function getObjCRightHandAssignmentParamType( | ||
param , | ||
index , | ||
) { | ||
const {typeAnnotation} = param; | ||
function getObjCRightHandAssignmentParamType(param, index) { | ||
const typeAnnotation = param.typeAnnotation; | ||
@@ -217,18 +245,25 @@ switch (typeAnnotation.type) { | ||
return `[(NSNumber *)arg${index} doubleValue]`; | ||
default: | ||
(typeAnnotation.name ); | ||
typeAnnotation.name; | ||
throw new Error(`Receieved invalid type: ${typeAnnotation.name}`); | ||
} | ||
case 'BooleanTypeAnnotation': | ||
return `[(NSNumber *)arg${index} boolValue]`; | ||
case 'DoubleTypeAnnotation': | ||
return `[(NSNumber *)arg${index} doubleValue]`; | ||
case 'FloatTypeAnnotation': | ||
return `[(NSNumber *)arg${index} floatValue]`; | ||
case 'Int32TypeAnnotation': | ||
return `[(NSNumber *)arg${index} intValue]`; | ||
case 'StringTypeAnnotation': | ||
return `(NSString *)arg${index}`; | ||
default: | ||
(typeAnnotation.type ); | ||
typeAnnotation.type; | ||
throw new Error('Received invalid param type annotation'); | ||
@@ -238,87 +273,49 @@ } | ||
function generateProtocol( | ||
component , | ||
componentName , | ||
) { | ||
const commands = component.commands | ||
.map(command => { | ||
const params = command.typeAnnotation.params; | ||
const paramString = | ||
params.length === 0 | ||
? '' | ||
: params | ||
.map((param, index) => { | ||
const objCType = getObjCParamType(param); | ||
return `${index === 0 ? '' : param.name}:(${objCType})${ | ||
param.name | ||
}`; | ||
}) | ||
.join(' '); | ||
return `- (void)${command.name}${paramString};`; | ||
}) | ||
.join('\n') | ||
.trim(); | ||
return protocolTemplate | ||
.replace(/::_COMPONENT_NAME_::/g, componentName) | ||
.replace('::_METHODS_::', commands); | ||
function generateProtocol(component, componentName) { | ||
const methods = component.commands.map(command => { | ||
const params = command.typeAnnotation.params; | ||
const paramString = params.length === 0 ? '' : params.map((param, index) => { | ||
const objCType = getObjCParamType(param); | ||
return `${index === 0 ? '' : param.name}:(${objCType})${param.name}`; | ||
}).join(' '); | ||
return `- (void)${command.name}${paramString};`; | ||
}).join('\n').trim(); | ||
return ProtocolTemplate({ | ||
componentName, | ||
methods | ||
}); | ||
} | ||
function generateConvertAndValidateParam( | ||
param , | ||
index , | ||
componentName , | ||
) { | ||
function generateConvertAndValidateParam(param, index, componentName) { | ||
const leftSideType = getObjCParamType(param); | ||
const expectedKind = getObjCExpectedKindParamType(param); | ||
const expectedKindString = getReadableExpectedKindParamType(param); | ||
const argConversion = `${leftSideType} ${ | ||
param.name | ||
} = ${getObjCRightHandAssignmentParamType(param, index)};`; | ||
return commandHandlerIfCaseConvertArgTemplate | ||
.replace(/::_COMPONENT_NAME_::/g, componentName) | ||
.replace('::_ARG_CONVERSION_::', argConversion) | ||
.replace(/::_ARG_NUMBER_::/g, '' + index) | ||
.replace('::_ARG_NUMBER_STR_::', getOrdinalNumber(index + 1)) | ||
.replace('::_EXPECTED_KIND_::', expectedKind) | ||
.replace('::_EXPECTED_KIND_STRING_::', expectedKindString); | ||
const argConversion = `${leftSideType} ${param.name} = ${getObjCRightHandAssignmentParamType(param, index)};`; | ||
return CommandHandlerIfCaseConvertArgTemplate({ | ||
componentName, | ||
argConversion, | ||
argNumber: index, | ||
argNumberString: getOrdinalNumber(index + 1), | ||
expectedKind, | ||
expectedKindString | ||
}); | ||
} | ||
function generateCommandIfCase( | ||
command , | ||
componentName , | ||
) { | ||
function generateCommandIfCase(command, componentName) { | ||
const params = command.typeAnnotation.params; | ||
const convertArgs = params | ||
.map((param, index) => | ||
generateConvertAndValidateParam(param, index, componentName), | ||
) | ||
.join('\n\n') | ||
.trim(); | ||
const commandCallArgs = | ||
params.length === 0 | ||
? '' | ||
: params | ||
.map((param, index) => { | ||
return `${index === 0 ? '' : param.name}:${param.name}`; | ||
}) | ||
.join(' '); | ||
const convertArgs = params.map((param, index) => generateConvertAndValidateParam(param, index, componentName)).join('\n\n').trim(); | ||
const commandCallArgs = params.length === 0 ? '' : params.map((param, index) => { | ||
return `${index === 0 ? '' : param.name}:${param.name}`; | ||
}).join(' '); | ||
const commandCall = `[componentView ${command.name}${commandCallArgs}];`; | ||
return commandHandlerIfCaseTemplate | ||
.replace(/::_COMPONENT_NAME_::/g, componentName) | ||
.replace(/::_COMMAND_NAME_::/g, command.name) | ||
.replace(/::_NUM_ARGS_::/g, '' + params.length) | ||
.replace('::_CONVERT_ARGS_::', convertArgs) | ||
.replace('::_COMMAND_CALL_::', commandCall); | ||
return CommandHandlerIfCaseTemplate({ | ||
componentName, | ||
commandName: command.name, | ||
numArgs: params.length, | ||
convertArgs, | ||
commandCall | ||
}); | ||
} | ||
function generateCommandHandler( | ||
component , | ||
componentName , | ||
) { | ||
function generateCommandHandler(component, componentName) { | ||
if (component.commands.length === 0) { | ||
@@ -328,61 +325,38 @@ return null; | ||
const ifCases = component.commands | ||
.map(command => generateCommandIfCase(command, componentName)) | ||
.join('\n\n'); | ||
return commandHandlerTemplate | ||
.replace(/::_COMPONENT_NAME_::/g, componentName) | ||
.replace('::_IF_CASES_::', ifCases); | ||
const ifCases = component.commands.map(command => generateCommandIfCase(command, componentName)).join('\n\n'); | ||
return CommandHandlerTemplate({ | ||
componentName, | ||
ifCases | ||
}); | ||
} | ||
module.exports = { | ||
generate( | ||
libraryName , | ||
schema , | ||
packageName , | ||
assumeNonnull = false, | ||
) { | ||
generate(libraryName, schema, packageName, assumeNonnull = false) { | ||
const fileName = 'RCTComponentViewHelpers.h'; | ||
const componentContent = Object.keys(schema.modules).map(moduleName => { | ||
const module = schema.modules[moduleName]; | ||
const componentContent = Object.keys(schema.modules) | ||
.map(moduleName => { | ||
const module = schema.modules[moduleName]; | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
const {components} = module; | ||
// No components in this module | ||
if (components == null) { | ||
return null; | ||
} | ||
const components = module.components; // No components in this module | ||
return Object.keys(components) | ||
.filter(componentName => { | ||
const component = components[componentName]; | ||
return !( | ||
component.excludedPlatforms && | ||
component.excludedPlatforms.includes('iOS') | ||
); | ||
}) | ||
.map(componentName => { | ||
return [ | ||
generateProtocol(components[componentName], componentName), | ||
generateCommandHandler(components[componentName], componentName), | ||
] | ||
.join('\n\n') | ||
.trim(); | ||
}) | ||
.join('\n\n'); | ||
}) | ||
.filter(Boolean) | ||
.join('\n\n'); | ||
if (components == null) { | ||
return null; | ||
} | ||
const replacedTemplate = template.replace( | ||
'::_COMPONENT_CONTENT_::', | ||
componentContent, | ||
); | ||
return Object.keys(components).filter(componentName => { | ||
const component = components[componentName]; | ||
return !(component.excludedPlatforms && component.excludedPlatforms.includes('iOS')); | ||
}).map(componentName => { | ||
return [generateProtocol(components[componentName], componentName), generateCommandHandler(components[componentName], componentName)].join('\n\n').trim(); | ||
}).join('\n\n'); | ||
}).filter(Boolean).join('\n\n'); | ||
const replacedTemplate = FileTemplate({ | ||
componentContent | ||
}); | ||
return new Map([[fileName, replacedTemplate]]); | ||
} | ||
return new Map([[fileName, replacedTemplate]]); | ||
}, | ||
}; | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,28 +10,17 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
const {generateEventStructName} = require('./CppHelpers.js'); | ||
const _require = require('./CppHelpers.js'), | ||
generateEventStructName = _require.generateEventStructName; // File path -> contents | ||
// File path -> contents | ||
const template = ` | ||
const FileTemplate = ({ | ||
events, | ||
libraryName | ||
}) => ` | ||
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* Do not edit this file as changes may cause incorrect behavior and will be lost | ||
* once the code is regenerated. | ||
* | ||
@@ -41,3 +30,3 @@ * ${'@'}generated by codegen project: GenerateEventEmitterCpp.js | ||
#include <react/renderer/components/::_LIBRARY_::/EventEmitters.h> | ||
#include <react/renderer/components/${libraryName}/EventEmitters.h> | ||
@@ -47,3 +36,3 @@ namespace facebook { | ||
::_EVENTS_:: | ||
${events} | ||
@@ -54,6 +43,12 @@ } // namespace react | ||
const componentTemplate = ` | ||
void ::_CLASSNAME_::EventEmitter::::_EVENT_NAME_::(::_STRUCT_NAME_:: event) const { | ||
dispatchEvent("::_DISPATCH_EVENT_NAME_::", [event=std::move(event)](jsi::Runtime &runtime) { | ||
::_IMPLEMENTATION_:: | ||
const ComponentTemplate = ({ | ||
className, | ||
eventName, | ||
structName, | ||
dispatchEventName, | ||
implementation | ||
}) => ` | ||
void ${className}EventEmitter::${eventName}(${structName} event) const { | ||
dispatchEvent("${dispatchEventName}", [event=std::move(event)](jsi::Runtime &runtime) { | ||
${implementation} | ||
}); | ||
@@ -63,5 +58,9 @@ } | ||
const basicComponentTemplate = ` | ||
void ::_CLASSNAME_::EventEmitter::::_EVENT_NAME_::() const { | ||
dispatchEvent("::_DISPATCH_EVENT_NAME_::"); | ||
const BasicComponentTemplate = ({ | ||
className, | ||
eventName, | ||
dispatchEventName | ||
}) => ` | ||
void ${className}EventEmitter::${eventName}() const { | ||
dispatchEvent("${dispatchEventName}"); | ||
} | ||
@@ -72,6 +71,3 @@ `.trim(); | ||
const trailingPeriod = propertyParts.length === 0 ? '' : '.'; | ||
const eventChain = `event.${propertyParts.join( | ||
'.', | ||
)}${trailingPeriod}${propertyName});`; | ||
const eventChain = `event.${propertyParts.join('.')}${trailingPeriod}${propertyName});`; | ||
return `${variableName}.setProperty(runtime, "${propertyName}", ${eventChain}`; | ||
@@ -82,64 +78,35 @@ } | ||
const trailingPeriod = propertyParts.length === 0 ? '' : '.'; | ||
const eventChain = `event.${propertyParts.join( | ||
'.', | ||
)}${trailingPeriod}${propertyName})`; | ||
const eventChain = `event.${propertyParts.join('.')}${trailingPeriod}${propertyName})`; | ||
return `${variableName}.setProperty(runtime, "${propertyName}", toString(${eventChain});`; | ||
} | ||
function generateSetters( | ||
parentPropertyName , | ||
properties , | ||
propertyParts , | ||
) { | ||
const propSetters = properties | ||
.map(eventProperty => { | ||
const {typeAnnotation} = eventProperty; | ||
switch (typeAnnotation.type) { | ||
case 'BooleanTypeAnnotation': | ||
return generateSetter( | ||
parentPropertyName, | ||
eventProperty.name, | ||
propertyParts, | ||
); | ||
case 'StringTypeAnnotation': | ||
return generateSetter( | ||
parentPropertyName, | ||
eventProperty.name, | ||
propertyParts, | ||
); | ||
case 'Int32TypeAnnotation': | ||
return generateSetter( | ||
parentPropertyName, | ||
eventProperty.name, | ||
propertyParts, | ||
); | ||
case 'DoubleTypeAnnotation': | ||
return generateSetter( | ||
parentPropertyName, | ||
eventProperty.name, | ||
propertyParts, | ||
); | ||
case 'FloatTypeAnnotation': | ||
return generateSetter( | ||
parentPropertyName, | ||
eventProperty.name, | ||
propertyParts, | ||
); | ||
case 'StringEnumTypeAnnotation': | ||
return generateEnumSetter( | ||
parentPropertyName, | ||
eventProperty.name, | ||
propertyParts, | ||
); | ||
case 'ObjectTypeAnnotation': | ||
const propertyName = eventProperty.name; | ||
return ` | ||
function generateSetters(parentPropertyName, properties, propertyParts) { | ||
const propSetters = properties.map(eventProperty => { | ||
const typeAnnotation = eventProperty.typeAnnotation; | ||
switch (typeAnnotation.type) { | ||
case 'BooleanTypeAnnotation': | ||
return generateSetter(parentPropertyName, eventProperty.name, propertyParts); | ||
case 'StringTypeAnnotation': | ||
return generateSetter(parentPropertyName, eventProperty.name, propertyParts); | ||
case 'Int32TypeAnnotation': | ||
return generateSetter(parentPropertyName, eventProperty.name, propertyParts); | ||
case 'DoubleTypeAnnotation': | ||
return generateSetter(parentPropertyName, eventProperty.name, propertyParts); | ||
case 'FloatTypeAnnotation': | ||
return generateSetter(parentPropertyName, eventProperty.name, propertyParts); | ||
case 'StringEnumTypeAnnotation': | ||
return generateEnumSetter(parentPropertyName, eventProperty.name, propertyParts); | ||
case 'ObjectTypeAnnotation': | ||
const propertyName = eventProperty.name; | ||
return ` | ||
{ | ||
auto ${propertyName} = jsi::Object(runtime); | ||
${generateSetters( | ||
propertyName, | ||
typeAnnotation.properties, | ||
propertyParts.concat([propertyName]), | ||
)} | ||
${generateSetters(propertyName, typeAnnotation.properties, propertyParts.concat([propertyName]))} | ||
@@ -149,13 +116,12 @@ ${parentPropertyName}.setProperty(runtime, "${propertyName}", ${propertyName}); | ||
`.trim(); | ||
default: | ||
(typeAnnotation.type ); | ||
throw new Error('Received invalid event property type'); | ||
} | ||
}) | ||
.join('\n'); | ||
default: | ||
typeAnnotation.type; | ||
throw new Error('Received invalid event property type'); | ||
} | ||
}).join('\n'); | ||
return propSetters; | ||
} | ||
function generateEvent(componentName , event) { | ||
function generateEvent(componentName, event) { | ||
// This is a gross hack necessary because native code is sending | ||
@@ -167,5 +133,3 @@ // events named things like topChange to JS which is then converted back to | ||
// throughout the system. | ||
const dispatchEventName = `${event.name[2].toLowerCase()}${event.name.slice( | ||
3, | ||
)}`; | ||
const dispatchEventName = `${event.name[2].toLowerCase()}${event.name.slice(3)}`; | ||
@@ -183,62 +147,49 @@ if (event.typeAnnotation.argument) { | ||
return componentTemplate | ||
.replace(/::_CLASSNAME_::/g, componentName) | ||
.replace(/::_EVENT_NAME_::/g, event.name) | ||
.replace(/::_DISPATCH_EVENT_NAME_::/g, dispatchEventName) | ||
.replace('::_STRUCT_NAME_::', generateEventStructName([event.name])) | ||
.replace('::_IMPLEMENTATION_::', implementation); | ||
return ComponentTemplate({ | ||
className: componentName, | ||
eventName: event.name, | ||
dispatchEventName, | ||
structName: generateEventStructName([event.name]), | ||
implementation | ||
}); | ||
} | ||
return basicComponentTemplate | ||
.replace(/::_CLASSNAME_::/g, componentName) | ||
.replace(/::_EVENT_NAME_::/g, event.name) | ||
.replace(/::_DISPATCH_EVENT_NAME_::/g, dispatchEventName); | ||
return BasicComponentTemplate({ | ||
className: componentName, | ||
eventName: event.name, | ||
dispatchEventName | ||
}); | ||
} | ||
module.exports = { | ||
generate( | ||
libraryName , | ||
schema , | ||
packageName , | ||
assumeNonnull = false, | ||
) { | ||
const moduleComponents = Object.keys(schema.modules) | ||
.map(moduleName => { | ||
const module = schema.modules[moduleName]; | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
generate(libraryName, schema, packageName, assumeNonnull = false) { | ||
const moduleComponents = Object.keys(schema.modules).map(moduleName => { | ||
const module = schema.modules[moduleName]; | ||
const {components} = module; | ||
// No components in this module | ||
if (components == null) { | ||
return null; | ||
} | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
return components; | ||
}) | ||
.filter(Boolean) | ||
.reduce((acc, components) => Object.assign(acc, components), {}); | ||
const components = module.components; // No components in this module | ||
if (components == null) { | ||
return null; | ||
} | ||
return components; | ||
}).filter(Boolean).reduce((acc, components) => Object.assign(acc, components), {}); | ||
const fileName = 'EventEmitters.cpp'; | ||
const componentEmitters = Object.keys(moduleComponents).map(componentName => { | ||
const component = moduleComponents[componentName]; | ||
return component.events.map(event => { | ||
return generateEvent(componentName, event); | ||
}).join('\n'); | ||
}).join('\n'); | ||
const replacedTemplate = FileTemplate({ | ||
libraryName, | ||
events: componentEmitters | ||
}); | ||
return new Map([[fileName, replacedTemplate]]); | ||
} | ||
const componentEmitters = Object.keys(moduleComponents) | ||
.map(componentName => { | ||
const component = moduleComponents[componentName]; | ||
return component.events | ||
.map(event => { | ||
return generateEvent(componentName, event); | ||
}) | ||
.join('\n'); | ||
}) | ||
.join('\n'); | ||
const replacedTemplate = template | ||
.replace(/::_COMPONENT_EMITTERS_::/g, componentEmitters) | ||
.replace('::_LIBRARY_::', libraryName) | ||
.replace('::_EVENTS_::', componentEmitters); | ||
return new Map([[fileName, replacedTemplate]]); | ||
}, | ||
}; | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,3 +10,2 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
@@ -16,31 +15,19 @@ | ||
const { | ||
getCppTypeForAnnotation, | ||
toSafeCppString, | ||
generateEventStructName, | ||
} = require('./CppHelpers.js'); | ||
const _require = require('./CppHelpers'), | ||
getCppTypeForAnnotation = _require.getCppTypeForAnnotation, | ||
toSafeCppString = _require.toSafeCppString, | ||
generateEventStructName = _require.generateEventStructName; | ||
const _require2 = require('../Utils'), | ||
indent = _require2.indent; // File path -> contents | ||
// File path -> contents | ||
const template = ` | ||
const FileTemplate = ({ | ||
componentEmitters | ||
}) => ` | ||
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* Do not edit this file as changes may cause incorrect behavior and will be lost | ||
* once the code is regenerated. | ||
* | ||
@@ -52,2 +39,3 @@ * ${'@'}generated by codegen project: GenerateEventEmitterH.js | ||
#include <react/renderer/components/view/ViewEventEmitter.h> | ||
#include <jsi/jsi.h> | ||
@@ -57,3 +45,3 @@ namespace facebook { | ||
::_COMPONENT_EMITTERS_:: | ||
${componentEmitters} | ||
@@ -64,26 +52,37 @@ } // namespace react | ||
const componentTemplate = ` | ||
class ::_CLASSNAME_::EventEmitter : public ViewEventEmitter { | ||
const ComponentTemplate = ({ | ||
className, | ||
structs, | ||
events | ||
}) => ` | ||
class JSI_EXPORT ${className}EventEmitter : public ViewEventEmitter { | ||
public: | ||
using ViewEventEmitter::ViewEventEmitter; | ||
::_STRUCTS_:: | ||
${structs} | ||
::_EVENTS_:: | ||
${events} | ||
}; | ||
`.trim(); | ||
const structTemplate = ` | ||
struct ::_STRUCT_NAME_:: { | ||
::_FIELDS_:: | ||
const StructTemplate = ({ | ||
structName, | ||
fields | ||
}) => ` | ||
struct ${structName} { | ||
${fields} | ||
}; | ||
`.trim(); | ||
const enumTemplate = `enum class ::_ENUM_NAME_:: { | ||
::_VALUES_:: | ||
const EnumTemplate = ({ | ||
enumName, | ||
values, | ||
toCases | ||
}) => `enum class ${enumName} { | ||
${values} | ||
}; | ||
static char const *toString(const ::_ENUM_NAME_:: value) { | ||
static char const *toString(const ${enumName} value) { | ||
switch (value) { | ||
::_TO_CASES_:: | ||
${toCases} | ||
} | ||
@@ -93,22 +92,5 @@ } | ||
function indent(nice , spaces ) { | ||
return nice | ||
.split('\n') | ||
.map((line, index) => { | ||
if (line.length === 0 || index === 0) { | ||
return line; | ||
} | ||
const emptySpaces = new Array(spaces + 1).join(' '); | ||
return emptySpaces + line; | ||
}) | ||
.join('\n'); | ||
} | ||
function getNativeTypeFromAnnotation(componentName, eventProperty, nameParts) { | ||
const type = eventProperty.typeAnnotation.type; | ||
function getNativeTypeFromAnnotation( | ||
componentName , | ||
eventProperty , | ||
nameParts , | ||
) { | ||
const {type} = eventProperty.typeAnnotation; | ||
switch (type) { | ||
@@ -121,113 +103,84 @@ case 'BooleanTypeAnnotation': | ||
return getCppTypeForAnnotation(type); | ||
case 'StringEnumTypeAnnotation': | ||
return generateEventStructName(nameParts.concat([eventProperty.name])); | ||
case 'ObjectTypeAnnotation': | ||
return generateEventStructName(nameParts.concat([eventProperty.name])); | ||
default: | ||
(type ); | ||
type; | ||
throw new Error(`Received invalid event property type ${type}`); | ||
} | ||
} | ||
function generateEnum(structs, options, nameParts) { | ||
const structName = generateEventStructName(nameParts); | ||
const fields = options | ||
.map((option, index) => `${toSafeCppString(option)}`) | ||
.join(',\n '); | ||
const toCases = options | ||
.map( | ||
option => | ||
`case ${structName}::${toSafeCppString(option)}: return "${option}";`, | ||
) | ||
.join('\n' + ' '); | ||
structs.set( | ||
structName, | ||
enumTemplate | ||
.replace(/::_ENUM_NAME_::/g, structName) | ||
.replace('::_VALUES_::', fields) | ||
.replace('::_TO_CASES_::', toCases), | ||
); | ||
const fields = options.map((option, index) => `${toSafeCppString(option)}`).join(',\n '); | ||
const toCases = options.map(option => `case ${structName}::${toSafeCppString(option)}: return "${option}";`).join('\n' + ' '); | ||
structs.set(structName, EnumTemplate({ | ||
enumName: structName, | ||
values: fields, | ||
toCases: toCases | ||
})); | ||
} | ||
function generateStruct( | ||
structs , | ||
componentName , | ||
nameParts , | ||
properties , | ||
) { | ||
function generateStruct(structs, componentName, nameParts, properties) { | ||
const structNameParts = nameParts; | ||
const structName = generateEventStructName(structNameParts); | ||
const fields = properties.map(property => { | ||
return `${getNativeTypeFromAnnotation(componentName, property, structNameParts)} ${property.name};`; | ||
}).join('\n' + ' '); | ||
properties.forEach(property => { | ||
const name = property.name, | ||
typeAnnotation = property.typeAnnotation; | ||
const fields = properties | ||
.map(property => { | ||
return `${getNativeTypeFromAnnotation( | ||
componentName, | ||
property, | ||
structNameParts, | ||
)} ${property.name};`; | ||
}) | ||
.join('\n' + ' '); | ||
properties.forEach(property => { | ||
const {name, typeAnnotation} = property; | ||
switch (typeAnnotation.type) { | ||
case 'BooleanTypeAnnotation': | ||
return; | ||
case 'StringTypeAnnotation': | ||
return; | ||
case 'Int32TypeAnnotation': | ||
return; | ||
case 'DoubleTypeAnnotation': | ||
return; | ||
case 'FloatTypeAnnotation': | ||
return; | ||
case 'ObjectTypeAnnotation': | ||
generateStruct( | ||
structs, | ||
componentName, | ||
nameParts.concat([name]), | ||
nullthrows(typeAnnotation.properties), | ||
); | ||
generateStruct(structs, componentName, nameParts.concat([name]), nullthrows(typeAnnotation.properties)); | ||
return; | ||
case 'StringEnumTypeAnnotation': | ||
generateEnum(structs, typeAnnotation.options, nameParts.concat([name])); | ||
return; | ||
default: | ||
(typeAnnotation.type ); | ||
throw new Error( | ||
`Received invalid event property type ${typeAnnotation.type}`, | ||
); | ||
typeAnnotation.type; | ||
throw new Error(`Received invalid event property type ${typeAnnotation.type}`); | ||
} | ||
}); | ||
structs.set( | ||
structs.set(structName, StructTemplate({ | ||
structName, | ||
structTemplate | ||
.replace('::_STRUCT_NAME_::', structName) | ||
.replace('::_FIELDS_::', fields), | ||
); | ||
fields | ||
})); | ||
} | ||
function generateStructs(componentName , component) { | ||
const structs = new Map(); | ||
function generateStructs(componentName, component) { | ||
const structs = new Map(); | ||
component.events.forEach(event => { | ||
if (event.typeAnnotation.argument) { | ||
generateStruct( | ||
structs, | ||
componentName, | ||
[event.name], | ||
event.typeAnnotation.argument.properties, | ||
); | ||
generateStruct(structs, componentName, [event.name], event.typeAnnotation.argument.properties); | ||
} | ||
}); | ||
return Array.from(structs.values()).join('\n\n'); | ||
} | ||
function generateEvent(componentName , event ) { | ||
function generateEvent(componentName, event) { | ||
if (event.typeAnnotation.argument) { | ||
const structName = generateEventStructName([event.name]); | ||
return `void ${event.name}(${structName} value) const;`; | ||
@@ -238,69 +191,41 @@ } | ||
} | ||
function generateEvents(componentName , component) { | ||
return component.events | ||
.map(event => generateEvent(componentName, event)) | ||
.join('\n\n' + ' '); | ||
function generateEvents(componentName, component) { | ||
return component.events.map(event => generateEvent(componentName, event)).join('\n\n' + ' '); | ||
} | ||
module.exports = { | ||
generate( | ||
libraryName , | ||
schema , | ||
packageName , | ||
assumeNonnull = false, | ||
) { | ||
const moduleComponents = Object.keys(schema.modules) | ||
.map(moduleName => { | ||
const module = schema.modules[moduleName]; | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
generate(libraryName, schema, packageName, assumeNonnull = false) { | ||
const moduleComponents = Object.keys(schema.modules).map(moduleName => { | ||
const module = schema.modules[moduleName]; | ||
const {components} = module; | ||
// No components in this module | ||
if (components == null) { | ||
return null; | ||
} | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
return components; | ||
}) | ||
.filter(Boolean) | ||
.reduce((acc, components) => Object.assign(acc, components), {}); | ||
const components = module.components; // No components in this module | ||
const moduleComponentsWithEvents = Object.keys(moduleComponents).filter( | ||
componentName => moduleComponents[componentName].events.length > 0, | ||
); | ||
if (components == null) { | ||
return null; | ||
} | ||
return components; | ||
}).filter(Boolean).reduce((acc, components) => Object.assign(acc, components), {}); | ||
const moduleComponentsWithEvents = Object.keys(moduleComponents); | ||
const fileName = 'EventEmitters.h'; | ||
const componentEmitters = moduleComponentsWithEvents.length > 0 ? Object.keys(moduleComponents).map(componentName => { | ||
const component = moduleComponents[componentName]; | ||
const replacedTemplate = ComponentTemplate({ | ||
className: componentName, | ||
structs: indent(generateStructs(componentName, component), 2), | ||
events: generateEvents(componentName, component) | ||
}); | ||
return replacedTemplate; | ||
}).join('\n') : ''; | ||
const replacedTemplate = FileTemplate({ | ||
componentEmitters | ||
}); | ||
return new Map([[fileName, replacedTemplate]]); | ||
} | ||
const componentEmitters = | ||
moduleComponentsWithEvents.length > 0 | ||
? Object.keys(moduleComponents) | ||
.map(componentName => { | ||
const component = moduleComponents[componentName]; | ||
const replacedTemplate = componentTemplate | ||
.replace(/::_CLASSNAME_::/g, componentName) | ||
.replace( | ||
'::_STRUCTS_::', | ||
indent(generateStructs(componentName, component), 2), | ||
) | ||
.replace( | ||
'::_EVENTS_::', | ||
generateEvents(componentName, component), | ||
) | ||
.trim(); | ||
return replacedTemplate; | ||
}) | ||
.join('\n') | ||
: ''; | ||
const replacedTemplate = template.replace( | ||
/::_COMPONENT_EMITTERS_::/g, | ||
componentEmitters, | ||
); | ||
return new Map([[fileName, replacedTemplate]]); | ||
}, | ||
}; | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,17 +10,19 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
const {convertDefaultTypeToString, getImports} = require('./CppHelpers'); | ||
const _require = require('./CppHelpers'), | ||
convertDefaultTypeToString = _require.convertDefaultTypeToString, | ||
getImports = _require.getImports; // File path -> contents | ||
// File path -> contents | ||
const template = ` | ||
const FileTemplate = ({ | ||
libraryName, | ||
imports, | ||
componentClasses | ||
}) => ` | ||
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* Do not edit this file as changes may cause incorrect behavior and will be lost | ||
* once the code is regenerated. | ||
* | ||
@@ -30,4 +32,4 @@ * ${'@'}generated by codegen project: GeneratePropsCpp.js | ||
#include <react/renderer/components/::_LIBRARY_::/Props.h> | ||
::_IMPORTS_:: | ||
#include <react/renderer/components/${libraryName}/Props.h> | ||
${imports} | ||
@@ -37,3 +39,3 @@ namespace facebook { | ||
::_COMPONENT_CLASSES_:: | ||
${componentClasses} | ||
@@ -44,43 +46,41 @@ } // namespace react | ||
const componentTemplate = ` | ||
::_CLASSNAME_::::::_CLASSNAME_::( | ||
const ComponentTemplate = ({ | ||
className, | ||
extendClasses, | ||
props | ||
}) => ` | ||
${className}::${className}( | ||
const PropsParserContext &context, | ||
const ::_CLASSNAME_:: &sourceProps, | ||
const RawProps &rawProps):::_EXTEND_CLASSES_:: | ||
const ${className} &sourceProps, | ||
const RawProps &rawProps):${extendClasses} | ||
::_PROPS_:: | ||
${props} | ||
{} | ||
`.trim(); | ||
function generatePropsString(componentName , component ) { | ||
return component.props | ||
.map(prop => { | ||
const defaultValue = convertDefaultTypeToString(componentName, prop); | ||
return `${prop.name}(convertRawProp(context, rawProps, "${prop.name}", sourceProps.${prop.name}, {${defaultValue}}))`; | ||
}) | ||
.join(',\n' + ' '); | ||
function generatePropsString(componentName, component) { | ||
return component.props.map(prop => { | ||
const defaultValue = convertDefaultTypeToString(componentName, prop); | ||
return `${prop.name}(convertRawProp(context, rawProps, "${prop.name}", sourceProps.${prop.name}, {${defaultValue}}))`; | ||
}).join(',\n' + ' '); | ||
} | ||
function getClassExtendString(component) { | ||
const extendString = | ||
' ' + | ||
component.extendsProps | ||
.map(extendProps => { | ||
switch (extendProps.type) { | ||
case 'ReactNativeBuiltInType': | ||
switch (extendProps.knownTypeName) { | ||
case 'ReactNativeCoreViewProps': | ||
return 'ViewProps(context, sourceProps, rawProps)'; | ||
default: | ||
(extendProps.knownTypeName ); | ||
throw new Error('Invalid knownTypeName'); | ||
} | ||
function getClassExtendString(component) { | ||
const extendString = ' ' + component.extendsProps.map(extendProps => { | ||
switch (extendProps.type) { | ||
case 'ReactNativeBuiltInType': | ||
switch (extendProps.knownTypeName) { | ||
case 'ReactNativeCoreViewProps': | ||
return 'ViewProps(context, sourceProps, rawProps)'; | ||
default: | ||
(extendProps.type ); | ||
throw new Error('Invalid extended type'); | ||
extendProps.knownTypeName; | ||
throw new Error('Invalid knownTypeName'); | ||
} | ||
}) | ||
.join(', ') + | ||
`${component.props.length > 0 ? ',' : ''}`; | ||
default: | ||
extendProps.type; | ||
throw new Error('Invalid extended type'); | ||
} | ||
}).join(', ') + `${component.props.length > 0 ? ',' : ''}`; | ||
return extendString; | ||
@@ -90,72 +90,42 @@ } | ||
module.exports = { | ||
generate( | ||
libraryName , | ||
schema , | ||
packageName , | ||
assumeNonnull = false, | ||
) { | ||
generate(libraryName, schema, packageName, assumeNonnull = false) { | ||
const fileName = 'Props.cpp'; | ||
const allImports = new Set([ | ||
'#include <react/renderer/core/propsConversions.h>', | ||
'#include <react/renderer/core/PropsParserContext.h>', | ||
]); | ||
const allImports = new Set(['#include <react/renderer/core/propsConversions.h>', '#include <react/renderer/core/PropsParserContext.h>']); | ||
const componentProps = Object.keys(schema.modules).map(moduleName => { | ||
const module = schema.modules[moduleName]; | ||
const componentProps = Object.keys(schema.modules) | ||
.map(moduleName => { | ||
const module = schema.modules[moduleName]; | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
const {components} = module; | ||
// No components in this module | ||
if (components == null) { | ||
return null; | ||
} | ||
const components = module.components; // No components in this module | ||
return Object.keys(components) | ||
.filter(componentName => { | ||
const component = components[componentName]; | ||
return !( | ||
component.excludedPlatforms && | ||
component.excludedPlatforms.includes('iOS') | ||
); | ||
}) | ||
.map(componentName => { | ||
const component = components[componentName]; | ||
const newName = `${componentName}Props`; | ||
if (components == null) { | ||
return null; | ||
} | ||
const propsString = generatePropsString(componentName, component); | ||
const extendString = getClassExtendString(component); | ||
return Object.keys(components).map(componentName => { | ||
const component = components[componentName]; | ||
const newName = `${componentName}Props`; | ||
const propsString = generatePropsString(componentName, component); | ||
const extendString = getClassExtendString(component); | ||
const imports = getImports(component.props); // $FlowFixMe[method-unbinding] added when improving typing for this parameters | ||
const imports = getImports(component.props); | ||
// $FlowFixMe[method-unbinding] added when improving typing for this parameters | ||
imports.forEach(allImports.add, allImports); | ||
imports.forEach(allImports.add, allImports); | ||
const replacedTemplate = ComponentTemplate({ | ||
className: newName, | ||
extendClasses: extendString, | ||
props: propsString | ||
}); | ||
return replacedTemplate; | ||
}).join('\n'); | ||
}).filter(Boolean).join('\n'); | ||
const replacedTemplate = FileTemplate({ | ||
componentClasses: componentProps, | ||
libraryName, | ||
imports: Array.from(allImports).sort().join('\n').trim() | ||
}); | ||
return new Map([[fileName, replacedTemplate]]); | ||
} | ||
const replacedTemplate = componentTemplate | ||
.replace(/::_CLASSNAME_::/g, newName) | ||
.replace('::_EXTEND_CLASSES_::', extendString) | ||
.replace('::_PROPS_::', propsString); | ||
return replacedTemplate; | ||
}) | ||
.join('\n'); | ||
}) | ||
.filter(Boolean) | ||
.join('\n'); | ||
const replacedTemplate = template | ||
.replace(/::_COMPONENT_CLASSES_::/g, componentProps) | ||
.replace('::_LIBRARY_::', libraryName) | ||
.replace( | ||
'::_IMPORTS_::', | ||
Array.from(allImports) | ||
.sort() | ||
.join('\n') | ||
.trim(), | ||
); | ||
return new Map([[fileName, replacedTemplate]]); | ||
}, | ||
}; | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,33 +10,26 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
const { | ||
convertDefaultTypeToString, | ||
getCppTypeForAnnotation, | ||
getEnumMaskName, | ||
getEnumName, | ||
toSafeCppString, | ||
generateStructName, | ||
getImports, | ||
toIntEnumValueName, | ||
} = require('./CppHelpers.js'); | ||
const _require = require('./ComponentsGeneratorUtils.js'), | ||
getNativeTypeFromAnnotation = _require.getNativeTypeFromAnnotation, | ||
getLocalImports = _require.getLocalImports; | ||
const _require2 = require('./CppHelpers.js'), | ||
convertDefaultTypeToString = _require2.convertDefaultTypeToString, | ||
getEnumMaskName = _require2.getEnumMaskName, | ||
getEnumName = _require2.getEnumName, | ||
toSafeCppString = _require2.toSafeCppString, | ||
generateStructName = _require2.generateStructName, | ||
toIntEnumValueName = _require2.toIntEnumValueName; // File path -> contents | ||
// File path -> contents | ||
const template = ` | ||
const FileTemplate = ({ | ||
imports, | ||
componentClasses | ||
}) => ` | ||
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* Do not edit this file as changes may cause incorrect behavior and will be lost | ||
* once the code is regenerated. | ||
* | ||
@@ -47,3 +40,3 @@ * ${'@'}generated by codegen project: GeneratePropsH.js | ||
::_IMPORTS_:: | ||
${imports} | ||
@@ -53,3 +46,3 @@ namespace facebook { | ||
::_COMPONENT_CLASSES_:: | ||
${componentClasses} | ||
@@ -60,28 +53,39 @@ } // namespace react | ||
const classTemplate = ` | ||
::_ENUMS_:: | ||
::_STRUCTS_:: | ||
class ::_CLASSNAME_:: final::_EXTEND_CLASSES_:: { | ||
const ClassTemplate = ({ | ||
enums, | ||
structs, | ||
className, | ||
props, | ||
extendClasses | ||
}) => ` | ||
${enums} | ||
${structs} | ||
class JSI_EXPORT ${className} final${extendClasses} { | ||
public: | ||
::_CLASSNAME_::() = default; | ||
::_CLASSNAME_::(const PropsParserContext& context, const ::_CLASSNAME_:: &sourceProps, const RawProps &rawProps); | ||
${className}() = default; | ||
${className}(const PropsParserContext& context, const ${className} &sourceProps, const RawProps &rawProps); | ||
#pragma mark - Props | ||
::_PROPS_:: | ||
${props} | ||
}; | ||
`.trim(); | ||
const enumTemplate = ` | ||
enum class ::_ENUM_NAME_:: { ::_VALUES_:: }; | ||
const EnumTemplate = ({ | ||
enumName, | ||
values, | ||
fromCases, | ||
toCases | ||
}) => ` | ||
enum class ${enumName} { ${values} }; | ||
static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ::_ENUM_NAME_:: &result) { | ||
static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ${enumName} &result) { | ||
auto string = (std::string)value; | ||
::_FROM_CASES_:: | ||
${fromCases} | ||
abort(); | ||
} | ||
static inline std::string toString(const ::_ENUM_NAME_:: &value) { | ||
static inline std::string toString(const ${enumName} &value) { | ||
switch (value) { | ||
::_TO_CASES_:: | ||
${toCases} | ||
} | ||
@@ -91,9 +95,14 @@ } | ||
const intEnumTemplate = ` | ||
enum class ::_ENUM_NAME_:: { ::_VALUES_:: }; | ||
const IntEnumTemplate = ({ | ||
enumName, | ||
values, | ||
fromCases, | ||
toCases | ||
}) => ` | ||
enum class ${enumName} { ${values} }; | ||
static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ::_ENUM_NAME_:: &result) { | ||
static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ${enumName} &result) { | ||
assert(value.hasType<int>()); | ||
auto integerValue = (int)value; | ||
switch (integerValue) {::_FROM_CASES_:: | ||
switch (integerValue) {${fromCases} | ||
} | ||
@@ -103,5 +112,5 @@ abort(); | ||
static inline std::string toString(const ::_ENUM_NAME_:: &value) { | ||
static inline std::string toString(const ${enumName} &value) { | ||
switch (value) { | ||
::_TO_CASES_:: | ||
${toCases} | ||
} | ||
@@ -111,21 +120,27 @@ } | ||
const structTemplate = `struct ::_STRUCT_NAME_:: { | ||
::_FIELDS_:: | ||
const StructTemplate = ({ | ||
structName, | ||
fields, | ||
fromCases | ||
}) => `struct ${structName} { | ||
${fields} | ||
}; | ||
static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ::_STRUCT_NAME_:: &result) { | ||
auto map = (better::map<std::string, RawValue>)value; | ||
static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ${structName} &result) { | ||
auto map = (butter::map<std::string, RawValue>)value; | ||
::_FROM_CASES_:: | ||
${fromCases} | ||
} | ||
static inline std::string toString(const ::_STRUCT_NAME_:: &value) { | ||
return "[Object ::_STRUCT_NAME_::]"; | ||
static inline std::string toString(const ${structName} &value) { | ||
return "[Object ${structName}]"; | ||
} | ||
`.trim(); | ||
const arrayConversionFunction = `static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, std::vector<::_STRUCT_NAME_::> &result) { | ||
const ArrayConversionFunctionTemplate = ({ | ||
structName | ||
}) => `static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, std::vector<${structName}> &result) { | ||
auto items = (std::vector<RawValue>)value; | ||
for (const auto &item : items) { | ||
::_STRUCT_NAME_:: newItem; | ||
${structName} newItem; | ||
fromRawValue(context, item, newItem); | ||
@@ -137,8 +152,10 @@ result.emplace_back(newItem); | ||
const doubleArrayConversionFunction = `static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, std::vector<std::vector<::_STRUCT_NAME_::>> &result) { | ||
const DoubleArrayConversionFunctionTemplate = ({ | ||
structName | ||
}) => `static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, std::vector<std::vector<${structName}>> &result) { | ||
auto items = (std::vector<std::vector<RawValue>>)value; | ||
for (const std::vector<RawValue> &item : items) { | ||
auto nestedArray = std::vector<::_STRUCT_NAME_::>{}; | ||
auto nestedArray = std::vector<${structName}>{}; | ||
for (const RawValue &nestedItem : item) { | ||
::_STRUCT_NAME_:: newItem; | ||
${structName} newItem; | ||
fromRawValue(context, nestedItem, newItem); | ||
@@ -152,31 +169,37 @@ nestedArray.emplace_back(newItem); | ||
const arrayEnumTemplate = ` | ||
using ::_ENUM_MASK_:: = uint32_t; | ||
const ArrayEnumTemplate = ({ | ||
enumName, | ||
enumMask, | ||
values, | ||
fromCases, | ||
toCases | ||
}) => ` | ||
using ${enumMask} = uint32_t; | ||
enum class ::_ENUM_NAME_::: ::_ENUM_MASK_:: { | ||
::_VALUES_:: | ||
enum class ${enumName}: ${enumMask} { | ||
${values} | ||
}; | ||
constexpr bool operator&( | ||
::_ENUM_MASK_:: const lhs, | ||
enum ::_ENUM_NAME_:: const rhs) { | ||
return lhs & static_cast<::_ENUM_MASK_::>(rhs); | ||
${enumMask} const lhs, | ||
enum ${enumName} const rhs) { | ||
return lhs & static_cast<${enumMask}>(rhs); | ||
} | ||
constexpr ::_ENUM_MASK_:: operator|( | ||
::_ENUM_MASK_:: const lhs, | ||
enum ::_ENUM_NAME_:: const rhs) { | ||
return lhs | static_cast<::_ENUM_MASK_::>(rhs); | ||
constexpr ${enumMask} operator|( | ||
${enumMask} const lhs, | ||
enum ${enumName} const rhs) { | ||
return lhs | static_cast<${enumMask}>(rhs); | ||
} | ||
constexpr void operator|=( | ||
::_ENUM_MASK_:: &lhs, | ||
enum ::_ENUM_NAME_:: const rhs) { | ||
lhs = lhs | static_cast<::_ENUM_MASK_::>(rhs); | ||
${enumMask} &lhs, | ||
enum ${enumName} const rhs) { | ||
lhs = lhs | static_cast<${enumMask}>(rhs); | ||
} | ||
static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ::_ENUM_MASK_:: &result) { | ||
static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, ${enumMask} &result) { | ||
auto items = std::vector<std::string>{value}; | ||
for (const auto &item : items) { | ||
::_FROM_CASES_:: | ||
${fromCases} | ||
abort(); | ||
@@ -186,7 +209,7 @@ } | ||
static inline std::string toString(const ::_ENUM_MASK_:: &value) { | ||
static inline std::string toString(const ${enumMask} &value) { | ||
auto result = std::string{}; | ||
auto separator = std::string{", "}; | ||
::_TO_CASES_:: | ||
${toCases} | ||
if (!result.empty()) { | ||
@@ -199,139 +222,48 @@ result.erase(result.length() - separator.length()); | ||
function getClassExtendString(component) { | ||
const extendString = | ||
' : ' + | ||
component.extendsProps | ||
.map(extendProps => { | ||
switch (extendProps.type) { | ||
case 'ReactNativeBuiltInType': | ||
switch (extendProps.knownTypeName) { | ||
case 'ReactNativeCoreViewProps': | ||
return 'public ViewProps'; | ||
default: | ||
(extendProps.knownTypeName ); | ||
throw new Error('Invalid knownTypeName'); | ||
} | ||
function getClassExtendString(component) { | ||
if (component.extendsProps.length === 0) { | ||
throw new Error('Invalid: component.extendsProps is empty'); | ||
} | ||
const extendString = ' : ' + component.extendsProps.map(extendProps => { | ||
switch (extendProps.type) { | ||
case 'ReactNativeBuiltInType': | ||
switch (extendProps.knownTypeName) { | ||
case 'ReactNativeCoreViewProps': | ||
return 'public ViewProps'; | ||
default: | ||
(extendProps.type ); | ||
throw new Error('Invalid extended type'); | ||
extendProps.knownTypeName; | ||
throw new Error('Invalid knownTypeName'); | ||
} | ||
}) | ||
.join(' '); | ||
default: | ||
extendProps.type; | ||
throw new Error('Invalid extended type'); | ||
} | ||
}).join(' '); | ||
return extendString; | ||
} | ||
function getNativeTypeFromAnnotation( | ||
componentName , | ||
prop, | ||
nameParts , | ||
) { | ||
const typeAnnotation = prop.typeAnnotation; | ||
switch (typeAnnotation.type) { | ||
case 'BooleanTypeAnnotation': | ||
case 'StringTypeAnnotation': | ||
case 'Int32TypeAnnotation': | ||
case 'DoubleTypeAnnotation': | ||
case 'FloatTypeAnnotation': | ||
return getCppTypeForAnnotation(typeAnnotation.type); | ||
case 'ReservedPropTypeAnnotation': | ||
switch (typeAnnotation.name) { | ||
case 'ColorPrimitive': | ||
return 'SharedColor'; | ||
case 'ImageSourcePrimitive': | ||
return 'ImageSource'; | ||
case 'PointPrimitive': | ||
return 'Point'; | ||
case 'EdgeInsetsPrimitive': | ||
return 'EdgeInsets'; | ||
default: | ||
(typeAnnotation.name ); | ||
throw new Error('Received unknown ReservedPropTypeAnnotation'); | ||
} | ||
case 'ArrayTypeAnnotation': { | ||
const arrayType = typeAnnotation.elementType.type; | ||
if (arrayType === 'ArrayTypeAnnotation') { | ||
return `std::vector<${getNativeTypeFromAnnotation( | ||
componentName, | ||
{typeAnnotation: typeAnnotation.elementType, name: ''}, | ||
nameParts.concat([prop.name]), | ||
)}>`; | ||
} | ||
if (arrayType === 'ObjectTypeAnnotation') { | ||
const structName = generateStructName( | ||
componentName, | ||
nameParts.concat([prop.name]), | ||
); | ||
return `std::vector<${structName}>`; | ||
} | ||
if (arrayType === 'StringEnumTypeAnnotation') { | ||
const enumName = getEnumName(componentName, prop.name); | ||
return getEnumMaskName(enumName); | ||
} | ||
const itemAnnotation = getNativeTypeFromAnnotation( | ||
componentName, | ||
{ | ||
typeAnnotation: typeAnnotation.elementType, | ||
name: componentName, | ||
}, | ||
nameParts.concat([prop.name]), | ||
); | ||
return `std::vector<${itemAnnotation}>`; | ||
} | ||
case 'ObjectTypeAnnotation': { | ||
return generateStructName(componentName, nameParts.concat([prop.name])); | ||
} | ||
case 'StringEnumTypeAnnotation': | ||
return getEnumName(componentName, prop.name); | ||
case 'Int32EnumTypeAnnotation': | ||
return getEnumName(componentName, prop.name); | ||
default: | ||
(typeAnnotation ); | ||
throw new Error( | ||
`Received invalid typeAnnotation for ${componentName} prop ${prop.name}, received ${typeAnnotation.type}`, | ||
); | ||
} | ||
} | ||
function convertValueToEnumOption(value ) { | ||
function convertValueToEnumOption(value) { | ||
return toSafeCppString(value); | ||
} | ||
function generateArrayEnumString( | ||
componentName , | ||
name , | ||
options , | ||
) { | ||
function generateArrayEnumString(componentName, name, options) { | ||
const enumName = getEnumName(componentName, name); | ||
const values = options | ||
.map((option, index) => `${toSafeCppString(option)} = 1 << ${index}`) | ||
.join(',\n '); | ||
const fromCases = options | ||
.map( | ||
option => | ||
`if (item == "${option}") { | ||
const values = options.map((option, index) => `${toSafeCppString(option)} = 1 << ${index}`).join(',\n '); | ||
const fromCases = options.map(option => `if (item == "${option}") { | ||
result |= ${enumName}::${toSafeCppString(option)}; | ||
continue; | ||
}`, | ||
) | ||
.join('\n '); | ||
const toCases = options | ||
.map( | ||
option => | ||
`if (value & ${enumName}::${toSafeCppString(option)}) { | ||
}`).join('\n '); | ||
const toCases = options.map(option => `if (value & ${enumName}::${toSafeCppString(option)}) { | ||
result += "${option}" + separator; | ||
}`, | ||
) | ||
.join('\n' + ' '); | ||
return arrayEnumTemplate | ||
.replace(/::_ENUM_NAME_::/g, enumName) | ||
.replace(/::_ENUM_MASK_::/g, getEnumMaskName(enumName)) | ||
.replace('::_VALUES_::', values) | ||
.replace('::_FROM_CASES_::', fromCases) | ||
.replace('::_TO_CASES_::', toCases); | ||
}`).join('\n' + ' '); | ||
return ArrayEnumTemplate({ | ||
enumName, | ||
enumMask: getEnumMaskName(enumName), | ||
values, | ||
fromCases, | ||
toCases | ||
}); | ||
} | ||
@@ -341,29 +273,14 @@ | ||
const typeAnnotation = prop.typeAnnotation; | ||
if (typeAnnotation.type === 'StringEnumTypeAnnotation') { | ||
const values = typeAnnotation.options; | ||
const values = typeAnnotation.options; | ||
const enumName = getEnumName(componentName, prop.name); | ||
const fromCases = values | ||
.map( | ||
value => | ||
`if (string == "${value}") { result = ${enumName}::${convertValueToEnumOption( | ||
value, | ||
)}; return; }`, | ||
) | ||
.join('\n' + ' '); | ||
const toCases = values | ||
.map( | ||
value => | ||
`case ${enumName}::${convertValueToEnumOption( | ||
value, | ||
)}: return "${value}";`, | ||
) | ||
.join('\n' + ' '); | ||
return enumTemplate | ||
.replace(/::_ENUM_NAME_::/g, enumName) | ||
.replace('::_VALUES_::', values.map(toSafeCppString).join(', ')) | ||
.replace('::_FROM_CASES_::', fromCases) | ||
.replace('::_TO_CASES_::', toCases); | ||
const fromCases = values.map(value => `if (string == "${value}") { result = ${enumName}::${convertValueToEnumOption(value)}; return; }`).join('\n' + ' '); | ||
const toCases = values.map(value => `case ${enumName}::${convertValueToEnumOption(value)}: return "${value}";`).join('\n' + ' '); | ||
return EnumTemplate({ | ||
enumName, | ||
values: values.map(toSafeCppString).join(', '), | ||
fromCases: fromCases, | ||
toCases: toCases | ||
}); | ||
} | ||
@@ -376,35 +293,18 @@ | ||
const typeAnnotation = prop.typeAnnotation; | ||
if (typeAnnotation.type === 'Int32EnumTypeAnnotation') { | ||
const values = typeAnnotation.options; | ||
const values = typeAnnotation.options; | ||
const enumName = getEnumName(componentName, prop.name); | ||
const fromCases = values | ||
.map( | ||
value => | ||
` | ||
const fromCases = values.map(value => ` | ||
case ${value}: | ||
result = ${enumName}::${toIntEnumValueName(prop.name, value)}; | ||
return;`, | ||
) | ||
.join(''); | ||
const toCases = values | ||
.map( | ||
value => | ||
`case ${enumName}::${toIntEnumValueName( | ||
prop.name, | ||
value, | ||
)}: return "${value}";`, | ||
) | ||
.join('\n' + ' '); | ||
const valueVariables = values | ||
.map(val => `${toIntEnumValueName(prop.name, val)} = ${val}`) | ||
.join(', '); | ||
return intEnumTemplate | ||
.replace(/::_ENUM_NAME_::/g, enumName) | ||
.replace('::_VALUES_::', valueVariables) | ||
.replace('::_FROM_CASES_::', fromCases) | ||
.replace('::_TO_CASES_::', toCases); | ||
return;`).join(''); | ||
const toCases = values.map(value => `case ${enumName}::${toIntEnumValueName(prop.name, value)}: return "${value}";`).join('\n' + ' '); | ||
const valueVariables = values.map(val => `${toIntEnumValueName(prop.name, val)} = ${val}`).join(', '); | ||
return IntEnumTemplate({ | ||
enumName, | ||
values: valueVariables, | ||
fromCases, | ||
toCases | ||
}); | ||
} | ||
@@ -415,65 +315,42 @@ | ||
function generateEnumString(componentName , component) { | ||
return component.props | ||
.map(prop => { | ||
if ( | ||
prop.typeAnnotation.type === 'ArrayTypeAnnotation' && | ||
prop.typeAnnotation.elementType.type === 'StringEnumTypeAnnotation' | ||
) { | ||
return generateArrayEnumString( | ||
componentName, | ||
prop.name, | ||
prop.typeAnnotation.elementType.options, | ||
); | ||
} | ||
function generateEnumString(componentName, component) { | ||
return component.props.map(prop => { | ||
if (prop.typeAnnotation.type === 'ArrayTypeAnnotation' && prop.typeAnnotation.elementType.type === 'StringEnumTypeAnnotation') { | ||
return generateArrayEnumString(componentName, prop.name, prop.typeAnnotation.elementType.options); | ||
} | ||
if (prop.typeAnnotation.type === 'StringEnumTypeAnnotation') { | ||
return generateStringEnum(componentName, prop); | ||
} | ||
if (prop.typeAnnotation.type === 'StringEnumTypeAnnotation') { | ||
return generateStringEnum(componentName, prop); | ||
} | ||
if (prop.typeAnnotation.type === 'Int32EnumTypeAnnotation') { | ||
return generateIntEnum(componentName, prop); | ||
} | ||
if (prop.typeAnnotation.type === 'Int32EnumTypeAnnotation') { | ||
return generateIntEnum(componentName, prop); | ||
} | ||
if (prop.typeAnnotation.type === 'ObjectTypeAnnotation') { | ||
return prop.typeAnnotation.properties | ||
.map(property => { | ||
if (property.typeAnnotation.type === 'StringEnumTypeAnnotation') { | ||
return generateStringEnum(componentName, property); | ||
} else if ( | ||
property.typeAnnotation.type === 'Int32EnumTypeAnnotation' | ||
) { | ||
return generateIntEnum(componentName, property); | ||
} | ||
return null; | ||
}) | ||
.filter(Boolean) | ||
.join('\n'); | ||
} | ||
}) | ||
.filter(Boolean) | ||
.join('\n'); | ||
if (prop.typeAnnotation.type === 'ObjectTypeAnnotation') { | ||
return prop.typeAnnotation.properties.map(property => { | ||
if (property.typeAnnotation.type === 'StringEnumTypeAnnotation') { | ||
return generateStringEnum(componentName, property); | ||
} else if (property.typeAnnotation.type === 'Int32EnumTypeAnnotation') { | ||
return generateIntEnum(componentName, property); | ||
} | ||
return null; | ||
}).filter(Boolean).join('\n'); | ||
} | ||
}).filter(Boolean).join('\n'); | ||
} | ||
function generatePropsString( | ||
componentName , | ||
props , | ||
) { | ||
return props | ||
.map(prop => { | ||
const nativeType = getNativeTypeFromAnnotation(componentName, prop, []); | ||
const defaultValue = convertDefaultTypeToString(componentName, prop); | ||
return `${nativeType} ${prop.name}{${defaultValue}};`; | ||
}) | ||
.join('\n' + ' '); | ||
function generatePropsString(componentName, props) { | ||
return props.map(prop => { | ||
const nativeType = getNativeTypeFromAnnotation(componentName, prop, []); | ||
const defaultValue = convertDefaultTypeToString(componentName, prop); | ||
return `${nativeType} ${prop.name}{${defaultValue}};`; | ||
}).join('\n' + ' '); | ||
} | ||
function getExtendsImports( | ||
extendsProps , | ||
) { | ||
const imports = new Set(); | ||
function getExtendsImports(extendsProps) { | ||
const imports = new Set(); | ||
imports.add('#include <react/renderer/core/PropsParserContext.h>'); | ||
imports.add('#include <jsi/jsi.h>'); | ||
extendsProps.forEach(extendProps => { | ||
@@ -484,109 +361,34 @@ switch (extendProps.type) { | ||
case 'ReactNativeCoreViewProps': | ||
imports.add( | ||
'#include <react/renderer/components/view/ViewProps.h>', | ||
); | ||
imports.add('#include <react/renderer/components/view/ViewProps.h>'); | ||
return; | ||
default: | ||
(extendProps.knownTypeName ); | ||
extendProps.knownTypeName; | ||
throw new Error('Invalid knownTypeName'); | ||
} | ||
default: | ||
(extendProps.type ); | ||
extendProps.type; | ||
throw new Error('Invalid extended type'); | ||
} | ||
}); | ||
return imports; | ||
} | ||
function getLocalImports( | ||
properties , | ||
) { | ||
const imports = new Set(); | ||
function addImportsForNativeName(name) { | ||
switch (name) { | ||
case 'ColorPrimitive': | ||
imports.add('#include <react/renderer/graphics/Color.h>'); | ||
return; | ||
case 'ImageSourcePrimitive': | ||
imports.add('#include <react/renderer/imagemanager/primitives.h>'); | ||
return; | ||
case 'PointPrimitive': | ||
imports.add('#include <react/renderer/graphics/Geometry.h>'); | ||
return; | ||
case 'EdgeInsetsPrimitive': | ||
imports.add('#include <react/renderer/graphics/Geometry.h>'); | ||
return; | ||
default: | ||
(name ); | ||
throw new Error(`Invalid ReservedPropTypeAnnotation name, got ${name}`); | ||
} | ||
} | ||
properties.forEach(prop => { | ||
const typeAnnotation = prop.typeAnnotation; | ||
if (typeAnnotation.type === 'ReservedPropTypeAnnotation') { | ||
addImportsForNativeName(typeAnnotation.name); | ||
} | ||
if (typeAnnotation.type === 'ArrayTypeAnnotation') { | ||
imports.add('#include <vector>'); | ||
if (typeAnnotation.elementType.type === 'StringEnumTypeAnnotation') { | ||
imports.add('#include <cinttypes>'); | ||
} | ||
} | ||
if ( | ||
typeAnnotation.type === 'ArrayTypeAnnotation' && | ||
typeAnnotation.elementType.type === 'ReservedPropTypeAnnotation' | ||
) { | ||
addImportsForNativeName(typeAnnotation.elementType.name); | ||
} | ||
if ( | ||
typeAnnotation.type === 'ArrayTypeAnnotation' && | ||
typeAnnotation.elementType.type === 'ObjectTypeAnnotation' | ||
) { | ||
const objectProps = typeAnnotation.elementType.properties; | ||
const objectImports = getImports(objectProps); | ||
const localImports = getLocalImports(objectProps); | ||
// $FlowFixMe[method-unbinding] added when improving typing for this parameters | ||
objectImports.forEach(imports.add, imports); | ||
// $FlowFixMe[method-unbinding] added when improving typing for this parameters | ||
localImports.forEach(imports.add, imports); | ||
} | ||
if (typeAnnotation.type === 'ObjectTypeAnnotation') { | ||
imports.add('#include <react/renderer/core/propsConversions.h>'); | ||
const objectImports = getImports(typeAnnotation.properties); | ||
const localImports = getLocalImports(typeAnnotation.properties); | ||
// $FlowFixMe[method-unbinding] added when improving typing for this parameters | ||
objectImports.forEach(imports.add, imports); | ||
// $FlowFixMe[method-unbinding] added when improving typing for this parameters | ||
localImports.forEach(imports.add, imports); | ||
} | ||
}); | ||
return imports; | ||
} | ||
function generateStructsForComponent(componentName , component) { | ||
function generateStructsForComponent(componentName, component) { | ||
const structs = generateStructs(componentName, component.props, []); | ||
const structArray = Array.from(structs.values()); | ||
if (structArray.length < 1) { | ||
return ''; | ||
} | ||
return structArray.join('\n\n'); | ||
} | ||
function generateStructs( | ||
componentName , | ||
properties, | ||
nameParts, | ||
) { | ||
const structs = new Map(); | ||
function generateStructs(componentName, properties, nameParts) { | ||
const structs = new Map(); | ||
properties.forEach(prop => { | ||
const typeAnnotation = prop.typeAnnotation; | ||
if (typeAnnotation.type === 'ObjectTypeAnnotation') { | ||
@@ -596,257 +398,162 @@ // Recursively visit all of the object properties. | ||
const elementProperties = typeAnnotation.properties; | ||
const nestedStructs = generateStructs( | ||
componentName, | ||
elementProperties, | ||
nameParts.concat([prop.name]), | ||
); | ||
nestedStructs.forEach(function(value, key) { | ||
const nestedStructs = generateStructs(componentName, elementProperties, nameParts.concat([prop.name])); | ||
nestedStructs.forEach(function (value, key) { | ||
structs.set(key, value); | ||
}); | ||
generateStruct( | ||
structs, | ||
componentName, | ||
nameParts.concat([prop.name]), | ||
typeAnnotation.properties, | ||
); | ||
generateStruct(structs, componentName, nameParts.concat([prop.name]), typeAnnotation.properties); | ||
} | ||
if ( | ||
prop.typeAnnotation.type === 'ArrayTypeAnnotation' && | ||
prop.typeAnnotation.elementType.type === 'ObjectTypeAnnotation' | ||
) { | ||
if (prop.typeAnnotation.type === 'ArrayTypeAnnotation' && prop.typeAnnotation.elementType.type === 'ObjectTypeAnnotation') { | ||
// Recursively visit all of the object properties. | ||
// Note: this is depth first so that the nested structs are ordered first. | ||
const elementProperties = prop.typeAnnotation.elementType.properties; | ||
const nestedStructs = generateStructs( | ||
componentName, | ||
elementProperties, | ||
nameParts.concat([prop.name]), | ||
); | ||
nestedStructs.forEach(function(value, key) { | ||
const nestedStructs = generateStructs(componentName, elementProperties, nameParts.concat([prop.name])); | ||
nestedStructs.forEach(function (value, key) { | ||
structs.set(key, value); | ||
}); | ||
}); // Generate this struct and its conversion function. | ||
// Generate this struct and its conversion function. | ||
generateStruct( | ||
structs, | ||
componentName, | ||
nameParts.concat([prop.name]), | ||
elementProperties, | ||
); | ||
generateStruct(structs, componentName, nameParts.concat([prop.name]), elementProperties); // Generate the conversion function for std:vector<Object>. | ||
// Note: This needs to be at the end since it references the struct above. | ||
// Generate the conversion function for std:vector<Object>. | ||
// Note: This needs to be at the end since it references the struct above. | ||
structs.set( | ||
`${[componentName, ...nameParts.concat([prop.name])].join( | ||
'', | ||
)}ArrayStruct`, | ||
arrayConversionFunction.replace( | ||
/::_STRUCT_NAME_::/g, | ||
generateStructName(componentName, nameParts.concat([prop.name])), | ||
), | ||
); | ||
structs.set(`${[componentName, ...nameParts.concat([prop.name])].join('')}ArrayStruct`, ArrayConversionFunctionTemplate({ | ||
structName: generateStructName(componentName, nameParts.concat([prop.name])) | ||
})); | ||
} | ||
if ( | ||
prop.typeAnnotation.type === 'ArrayTypeAnnotation' && | ||
prop.typeAnnotation.elementType.type === 'ArrayTypeAnnotation' && | ||
prop.typeAnnotation.elementType.elementType.type === | ||
'ObjectTypeAnnotation' | ||
) { | ||
if (prop.typeAnnotation.type === 'ArrayTypeAnnotation' && prop.typeAnnotation.elementType.type === 'ArrayTypeAnnotation' && prop.typeAnnotation.elementType.elementType.type === 'ObjectTypeAnnotation') { | ||
// Recursively visit all of the object properties. | ||
// Note: this is depth first so that the nested structs are ordered first. | ||
const elementProperties = | ||
prop.typeAnnotation.elementType.elementType.properties; | ||
const nestedStructs = generateStructs( | ||
componentName, | ||
elementProperties, | ||
nameParts.concat([prop.name]), | ||
); | ||
nestedStructs.forEach(function(value, key) { | ||
const elementProperties = prop.typeAnnotation.elementType.elementType.properties; | ||
const nestedStructs = generateStructs(componentName, elementProperties, nameParts.concat([prop.name])); | ||
nestedStructs.forEach(function (value, key) { | ||
structs.set(key, value); | ||
}); | ||
}); // Generate this struct and its conversion function. | ||
// Generate this struct and its conversion function. | ||
generateStruct( | ||
structs, | ||
componentName, | ||
nameParts.concat([prop.name]), | ||
elementProperties, | ||
); | ||
generateStruct(structs, componentName, nameParts.concat([prop.name]), elementProperties); // Generate the conversion function for std:vector<Object>. | ||
// Note: This needs to be at the end since it references the struct above. | ||
// Generate the conversion function for std:vector<Object>. | ||
// Note: This needs to be at the end since it references the struct above. | ||
structs.set( | ||
`${[componentName, ...nameParts.concat([prop.name])].join( | ||
'', | ||
)}ArrayArrayStruct`, | ||
doubleArrayConversionFunction.replace( | ||
/::_STRUCT_NAME_::/g, | ||
generateStructName(componentName, nameParts.concat([prop.name])), | ||
), | ||
); | ||
structs.set(`${[componentName, ...nameParts.concat([prop.name])].join('')}ArrayArrayStruct`, DoubleArrayConversionFunctionTemplate({ | ||
structName: generateStructName(componentName, nameParts.concat([prop.name])) | ||
})); | ||
} | ||
}); | ||
return structs; | ||
} | ||
function generateStruct( | ||
structs , | ||
componentName , | ||
nameParts , | ||
properties , | ||
) { | ||
function generateStruct(structs, componentName, nameParts, properties) { | ||
const structNameParts = nameParts; | ||
const structName = generateStructName(componentName, structNameParts); | ||
const fields = properties.map(property => { | ||
return `${getNativeTypeFromAnnotation(componentName, property, structNameParts)} ${property.name};`; | ||
}).join('\n' + ' '); | ||
properties.forEach(property => { | ||
const name = property.name; | ||
const fields = properties | ||
.map(property => { | ||
return `${getNativeTypeFromAnnotation( | ||
componentName, | ||
property, | ||
structNameParts, | ||
)} ${property.name};`; | ||
}) | ||
.join('\n' + ' '); | ||
properties.forEach((property ) => { | ||
const name = property.name; | ||
switch (property.typeAnnotation.type) { | ||
case 'BooleanTypeAnnotation': | ||
return; | ||
case 'StringTypeAnnotation': | ||
return; | ||
case 'Int32TypeAnnotation': | ||
return; | ||
case 'DoubleTypeAnnotation': | ||
return; | ||
case 'FloatTypeAnnotation': | ||
return; | ||
case 'ReservedPropTypeAnnotation': | ||
return; | ||
case 'ArrayTypeAnnotation': | ||
return; | ||
case 'StringEnumTypeAnnotation': | ||
return; | ||
case 'Int32EnumTypeAnnotation': | ||
return; | ||
case 'DoubleTypeAnnotation': | ||
return; | ||
case 'ObjectTypeAnnotation': | ||
const props = property.typeAnnotation.properties; | ||
if (props == null) { | ||
throw new Error( | ||
`Properties are expected for ObjectTypeAnnotation (see ${name} in ${componentName})`, | ||
); | ||
throw new Error(`Properties are expected for ObjectTypeAnnotation (see ${name} in ${componentName})`); | ||
} | ||
generateStruct(structs, componentName, nameParts.concat([name]), props); | ||
return; | ||
default: | ||
(property.typeAnnotation.type ); | ||
throw new Error( | ||
`Received invalid component property type ${property.typeAnnotation.type}`, | ||
); | ||
property.typeAnnotation.type; | ||
throw new Error(`Received invalid component property type ${property.typeAnnotation.type}`); | ||
} | ||
}); | ||
const fromCases = properties | ||
.map(property => { | ||
const variable = property.name; | ||
return `auto ${variable} = map.find("${property.name}"); | ||
const fromCases = properties.map(property => { | ||
const variable = 'tmp_' + property.name; | ||
return `auto ${variable} = map.find("${property.name}"); | ||
if (${variable} != map.end()) { | ||
fromRawValue(context, ${variable}->second, result.${variable}); | ||
fromRawValue(context, ${variable}->second, result.${property.name}); | ||
}`; | ||
}) | ||
.join('\n '); | ||
structs.set( | ||
}).join('\n '); | ||
structs.set(structName, StructTemplate({ | ||
structName, | ||
structTemplate | ||
.replace(/::_STRUCT_NAME_::/g, structName) | ||
.replace('::_FIELDS_::', fields) | ||
.replace('::_FROM_CASES_::', fromCases), | ||
); | ||
fields, | ||
fromCases | ||
})); | ||
} | ||
module.exports = { | ||
generate( | ||
libraryName , | ||
schema , | ||
packageName , | ||
assumeNonnull = false, | ||
) { | ||
generate(libraryName, schema, packageName, assumeNonnull = false) { | ||
const fileName = 'Props.h'; | ||
const allImports = new Set(); | ||
const componentClasses = Object.keys(schema.modules).map(moduleName => { | ||
const module = schema.modules[moduleName]; | ||
const allImports = new Set(); | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
const componentClasses = Object.keys(schema.modules) | ||
.map(moduleName => { | ||
const module = schema.modules[moduleName]; | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
const components = module.components; // No components in this module | ||
const {components} = module; | ||
// No components in this module | ||
if (components == null) { | ||
return null; | ||
} | ||
if (components == null) { | ||
return null; | ||
} | ||
return Object.keys(components) | ||
.filter(componentName => { | ||
const component = components[componentName]; | ||
return !( | ||
component.excludedPlatforms && | ||
component.excludedPlatforms.includes('iOS') | ||
); | ||
}) | ||
.map(componentName => { | ||
const component = components[componentName]; | ||
return Object.keys(components).map(componentName => { | ||
const component = components[componentName]; | ||
const newName = `${componentName}Props`; | ||
const structString = generateStructsForComponent(componentName, component); | ||
const enumString = generateEnumString(componentName, component); | ||
const propsString = generatePropsString(componentName, component.props); | ||
const extendString = getClassExtendString(component); | ||
const extendsImports = getExtendsImports(component.extendsProps); | ||
const imports = getLocalImports(component.props); // $FlowFixMe[method-unbinding] added when improving typing for this parameters | ||
const newName = `${componentName}Props`; | ||
const structString = generateStructsForComponent( | ||
componentName, | ||
component, | ||
); | ||
const enumString = generateEnumString(componentName, component); | ||
const propsString = generatePropsString( | ||
componentName, | ||
component.props, | ||
); | ||
const extendString = getClassExtendString(component); | ||
const extendsImports = getExtendsImports(component.extendsProps); | ||
const imports = getLocalImports(component.props); | ||
extendsImports.forEach(allImports.add, allImports); // $FlowFixMe[method-unbinding] added when improving typing for this parameters | ||
// $FlowFixMe[method-unbinding] added when improving typing for this parameters | ||
extendsImports.forEach(allImports.add, allImports); | ||
// $FlowFixMe[method-unbinding] added when improving typing for this parameters | ||
imports.forEach(allImports.add, allImports); | ||
imports.forEach(allImports.add, allImports); | ||
const replacedTemplate = ClassTemplate({ | ||
enums: enumString, | ||
structs: structString, | ||
className: newName, | ||
extendClasses: extendString, | ||
props: propsString | ||
}); | ||
return replacedTemplate; | ||
}).join('\n\n'); | ||
}).filter(Boolean).join('\n\n'); | ||
const replacedTemplate = FileTemplate({ | ||
componentClasses, | ||
imports: Array.from(allImports).sort().join('\n') | ||
}); | ||
return new Map([[fileName, replacedTemplate]]); | ||
} | ||
const replacedTemplate = classTemplate | ||
.replace('::_ENUMS_::', enumString) | ||
.replace('::_STRUCTS_::', structString) | ||
.replace(/::_CLASSNAME_::/g, newName) | ||
.replace('::_EXTEND_CLASSES_::', extendString) | ||
.replace('::_PROPS_::', propsString) | ||
.trim(); | ||
return replacedTemplate; | ||
}) | ||
.join('\n\n'); | ||
}) | ||
.filter(Boolean) | ||
.join('\n\n'); | ||
const replacedTemplate = template | ||
.replace(/::_COMPONENT_CLASSES_::/g, componentClasses) | ||
.replace( | ||
'::_IMPORTS_::', | ||
Array.from(allImports) | ||
.sort() | ||
.join('\n'), | ||
); | ||
return new Map([[fileName, replacedTemplate]]); | ||
}, | ||
}; | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,27 +10,23 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
const { | ||
getImports, | ||
toSafeJavaString, | ||
getInterfaceJavaClassName, | ||
getDelegateJavaClassName, | ||
} = require('./JavaHelpers'); | ||
const _require = require('./JavaHelpers'), | ||
getImports = _require.getImports, | ||
toSafeJavaString = _require.toSafeJavaString, | ||
getInterfaceJavaClassName = _require.getInterfaceJavaClassName, | ||
getDelegateJavaClassName = _require.getDelegateJavaClassName; // File path -> contents | ||
// File path -> contents | ||
const template = `/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
const FileTemplate = ({ | ||
packageName, | ||
imports, | ||
className, | ||
extendClasses, | ||
interfaceClassName, | ||
methods | ||
}) => `/** | ||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* Do not edit this file as changes may cause incorrect behavior and will be lost | ||
* once the code is regenerated. | ||
* | ||
@@ -40,34 +36,35 @@ * ${'@'}generated by codegen project: GeneratePropsJavaDelegate.js | ||
package ::_PACKAGE_NAME_::; | ||
package ${packageName}; | ||
::_IMPORTS_:: | ||
${imports} | ||
public class ::_CLASSNAME_::<T extends ::_EXTEND_CLASSES_::, U extends BaseViewManagerInterface<T> & ::_INTERFACE_CLASSNAME_::<T>> extends BaseViewManagerDelegate<T, U> { | ||
public ::_CLASSNAME_::(U viewManager) { | ||
public class ${className}<T extends ${extendClasses}, U extends BaseViewManagerInterface<T> & ${interfaceClassName}<T>> extends BaseViewManagerDelegate<T, U> { | ||
public ${className}(U viewManager) { | ||
super(viewManager); | ||
} | ||
::_METHODS_:: | ||
${methods} | ||
} | ||
`; | ||
const propSetterTemplate = ` | ||
const PropSetterTemplate = ({ | ||
propCases | ||
}) => ` | ||
@Override | ||
public void setProperty(T view, String propName, @Nullable Object value) { | ||
::_PROP_CASES_:: | ||
${propCases} | ||
} | ||
`; | ||
`.trim(); | ||
const commandsTemplate = ` | ||
const CommandsTemplate = ({ | ||
commandCases | ||
}) => ` | ||
@Override | ||
public void receiveCommand(T view, String commandName, ReadableArray args) { | ||
switch (commandName) { | ||
::_COMMAND_CASES_:: | ||
${commandCases} | ||
} | ||
} | ||
`; | ||
`.trim(); | ||
function getJavaValueForProp( | ||
prop , | ||
componentName , | ||
) { | ||
function getJavaValueForProp(prop, componentName) { | ||
const typeAnnotation = prop.typeAnnotation; | ||
@@ -82,10 +79,10 @@ | ||
} | ||
case 'StringTypeAnnotation': | ||
const defaultValueString = | ||
typeAnnotation.default === null | ||
? 'null' | ||
: `"${typeAnnotation.default}"`; | ||
const defaultValueString = typeAnnotation.default === null ? 'null' : `"${typeAnnotation.default}"`; | ||
return `value == null ? ${defaultValueString} : (String) value`; | ||
case 'Int32TypeAnnotation': | ||
return `value == null ? ${typeAnnotation.default} : ((Double) value).intValue()`; | ||
case 'DoubleTypeAnnotation': | ||
@@ -97,2 +94,3 @@ if (prop.optional) { | ||
} | ||
case 'FloatTypeAnnotation': | ||
@@ -106,2 +104,3 @@ if (typeAnnotation.default === null) { | ||
} | ||
case 'ReservedPropTypeAnnotation': | ||
@@ -111,24 +110,38 @@ switch (typeAnnotation.name) { | ||
return 'ColorPropConverter.getColor(value, view.getContext())'; | ||
case 'ImageSourcePrimitive': | ||
return '(ReadableMap) value'; | ||
case 'ImageRequestPrimitive': | ||
return '(ReadableMap) value'; | ||
case 'PointPrimitive': | ||
return '(ReadableMap) value'; | ||
case 'EdgeInsetsPrimitive': | ||
return '(ReadableMap) value'; | ||
default: | ||
(typeAnnotation.name ); | ||
typeAnnotation.name; | ||
throw new Error('Received unknown ReservedPropTypeAnnotation'); | ||
} | ||
case 'ArrayTypeAnnotation': { | ||
return '(ReadableArray) value'; | ||
} | ||
case 'ObjectTypeAnnotation': { | ||
return '(ReadableMap) value'; | ||
} | ||
case 'ArrayTypeAnnotation': | ||
{ | ||
return '(ReadableArray) value'; | ||
} | ||
case 'ObjectTypeAnnotation': | ||
{ | ||
return '(ReadableMap) value'; | ||
} | ||
case 'StringEnumTypeAnnotation': | ||
return '(String) value'; | ||
case 'Int32EnumTypeAnnotation': | ||
return `value == null ? ${typeAnnotation.default} : ((Double) value).intValue()`; | ||
default: | ||
(typeAnnotation ); | ||
typeAnnotation; | ||
throw new Error('Received invalid typeAnnotation'); | ||
@@ -138,6 +151,3 @@ } | ||
function generatePropCasesString( | ||
component , | ||
componentName , | ||
) { | ||
function generatePropCasesString(component, componentName) { | ||
if (component.props.length === 0) { | ||
@@ -147,12 +157,7 @@ return 'super.setProperty(view, propName, value);'; | ||
const cases = component.props | ||
.map(prop => { | ||
return `case "${prop.name}": | ||
mViewManager.set${toSafeJavaString( | ||
prop.name, | ||
)}(view, ${getJavaValueForProp(prop, componentName)}); | ||
const cases = component.props.map(prop => { | ||
return `case "${prop.name}": | ||
mViewManager.set${toSafeJavaString(prop.name)}(view, ${getJavaValueForProp(prop, componentName)}); | ||
break;`; | ||
}) | ||
.join('\n' + ' '); | ||
}).join('\n' + ' '); | ||
return `switch (propName) { | ||
@@ -166,3 +171,3 @@ ${cases} | ||
function getCommandArgJavaType(param, index) { | ||
const {typeAnnotation} = param; | ||
const typeAnnotation = param.typeAnnotation; | ||
@@ -174,18 +179,25 @@ switch (typeAnnotation.type) { | ||
return `args.getDouble(${index})`; | ||
default: | ||
(typeAnnotation.name ); | ||
typeAnnotation.name; | ||
throw new Error(`Receieved invalid type: ${typeAnnotation.name}`); | ||
} | ||
case 'BooleanTypeAnnotation': | ||
return `args.getBoolean(${index})`; | ||
case 'DoubleTypeAnnotation': | ||
return `args.getDouble(${index})`; | ||
case 'FloatTypeAnnotation': | ||
return `(float) args.getDouble(${index})`; | ||
case 'Int32TypeAnnotation': | ||
return `args.getInt(${index})`; | ||
case 'StringTypeAnnotation': | ||
return `args.getString(${index})`; | ||
default: | ||
(typeAnnotation.type ); | ||
typeAnnotation.type; | ||
throw new Error(`Receieved invalid type: ${typeAnnotation.type}`); | ||
@@ -195,15 +207,7 @@ } | ||
function getCommandArguments( | ||
command , | ||
) { | ||
return [ | ||
'view', | ||
...command.typeAnnotation.params.map(getCommandArgJavaType), | ||
].join(', '); | ||
function getCommandArguments(command) { | ||
return ['view', ...command.typeAnnotation.params.map(getCommandArgJavaType)].join(', '); | ||
} | ||
function generateCommandCasesString( | ||
component , | ||
componentName , | ||
) { | ||
function generateCommandCasesString(component, componentName) { | ||
if (component.commands.length === 0) { | ||
@@ -213,35 +217,28 @@ return null; | ||
const commandMethods = component.commands | ||
.map(command => { | ||
return `case "${command.name}": | ||
mViewManager.${toSafeJavaString( | ||
command.name, | ||
false, | ||
)}(${getCommandArguments(command)}); | ||
const commandMethods = component.commands.map(command => { | ||
return `case "${command.name}": | ||
mViewManager.${toSafeJavaString(command.name, false)}(${getCommandArguments(command)}); | ||
break;`; | ||
}) | ||
.join('\n' + ' '); | ||
}).join('\n' + ' '); | ||
return commandMethods; | ||
} | ||
function getClassExtendString(component) { | ||
const extendString = component.extendsProps | ||
.map(extendProps => { | ||
switch (extendProps.type) { | ||
case 'ReactNativeBuiltInType': | ||
switch (extendProps.knownTypeName) { | ||
case 'ReactNativeCoreViewProps': | ||
return 'View'; | ||
default: | ||
(extendProps.knownTypeName ); | ||
throw new Error('Invalid knownTypeName'); | ||
} | ||
default: | ||
(extendProps.type ); | ||
throw new Error('Invalid extended type'); | ||
} | ||
}) | ||
.join(''); | ||
function getClassExtendString(component) { | ||
const extendString = component.extendsProps.map(extendProps => { | ||
switch (extendProps.type) { | ||
case 'ReactNativeBuiltInType': | ||
switch (extendProps.knownTypeName) { | ||
case 'ReactNativeCoreViewProps': | ||
return 'View'; | ||
default: | ||
extendProps.knownTypeName; | ||
throw new Error('Invalid knownTypeName'); | ||
} | ||
default: | ||
extendProps.type; | ||
throw new Error('Invalid extended type'); | ||
} | ||
}).join(''); | ||
return extendString; | ||
@@ -251,40 +248,32 @@ } | ||
function getDelegateImports(component) { | ||
const imports = getImports(component, 'delegate'); | ||
// The delegate needs ReadableArray for commands always. | ||
const imports = getImports(component, 'delegate'); // The delegate needs ReadableArray for commands always. | ||
// The interface doesn't always need it | ||
if (component.commands.length > 0) { | ||
imports.add('import com.facebook.react.bridge.ReadableArray;'); | ||
} | ||
imports.add('import androidx.annotation.Nullable;'); | ||
imports.add('import com.facebook.react.uimanager.BaseViewManagerDelegate;'); | ||
imports.add('import com.facebook.react.uimanager.BaseViewManagerInterface;'); | ||
return imports; | ||
} | ||
function generateMethods(propsString, commandsString) { | ||
return [ | ||
propSetterTemplate.trim().replace('::_PROP_CASES_::', propsString), | ||
commandsString != null | ||
? commandsTemplate.trim().replace('::_COMMAND_CASES_::', commandsString) | ||
: '', | ||
] | ||
.join('\n\n ') | ||
.trimRight(); | ||
function generateMethods(propsString, commandsString) { | ||
return [PropSetterTemplate({ | ||
propCases: propsString | ||
}), commandsString != null ? CommandsTemplate({ | ||
commandCases: commandsString | ||
}) : ''].join('\n\n ').trimRight(); | ||
} | ||
module.exports = { | ||
generate( | ||
libraryName , | ||
schema , | ||
packageName , | ||
assumeNonnull = false, | ||
) { | ||
generate(libraryName, schema, packageName, assumeNonnull = false) { | ||
// TODO: This doesn't support custom package name yet. | ||
const normalizedPackageName = 'com.facebook.react.viewmanagers'; | ||
const outputDir = `java/${normalizedPackageName.replace(/\./g, '/')}`; | ||
const files = new Map(); | ||
Object.keys(schema.modules).forEach(moduleName => { | ||
const module = schema.modules[moduleName]; | ||
if (module.type !== 'Component') { | ||
@@ -294,4 +283,4 @@ return; | ||
const {components} = module; | ||
// No components in this module | ||
const components = module.components; // No components in this module | ||
if (components == null) { | ||
@@ -301,46 +290,27 @@ return; | ||
return Object.keys(components) | ||
.filter(componentName => { | ||
const component = components[componentName]; | ||
return !( | ||
component.excludedPlatforms && | ||
component.excludedPlatforms.includes('android') | ||
); | ||
}) | ||
.forEach(componentName => { | ||
const component = components[componentName]; | ||
const className = getDelegateJavaClassName(componentName); | ||
const interfaceClassName = getInterfaceJavaClassName(componentName); | ||
const imports = getDelegateImports(component); | ||
const propsString = generatePropCasesString(component, componentName); | ||
const commandsString = generateCommandCasesString( | ||
component, | ||
componentName, | ||
); | ||
const extendString = getClassExtendString(component); | ||
const replacedTemplate = template | ||
.replace( | ||
/::_IMPORTS_::/g, | ||
Array.from(imports) | ||
.sort() | ||
.join('\n'), | ||
) | ||
.replace(/::_PACKAGE_NAME_::/g, normalizedPackageName) | ||
.replace(/::_CLASSNAME_::/g, className) | ||
.replace('::_EXTEND_CLASSES_::', extendString) | ||
.replace('::_PROP_CASES_::', propsString) | ||
.replace( | ||
'::_METHODS_::', | ||
generateMethods(propsString, commandsString), | ||
) | ||
.replace(/::_INTERFACE_CLASSNAME_::/g, interfaceClassName); | ||
files.set(`${outputDir}/${className}.java`, replacedTemplate); | ||
return Object.keys(components).filter(componentName => { | ||
const component = components[componentName]; | ||
return !(component.excludedPlatforms && component.excludedPlatforms.includes('android')); | ||
}).forEach(componentName => { | ||
const component = components[componentName]; | ||
const className = getDelegateJavaClassName(componentName); | ||
const interfaceClassName = getInterfaceJavaClassName(componentName); | ||
const imports = getDelegateImports(component); | ||
const propsString = generatePropCasesString(component, componentName); | ||
const commandsString = generateCommandCasesString(component, componentName); | ||
const extendString = getClassExtendString(component); | ||
const replacedTemplate = FileTemplate({ | ||
imports: Array.from(imports).sort().join('\n'), | ||
packageName: normalizedPackageName, | ||
className, | ||
extendClasses: extendString, | ||
methods: generateMethods(propsString, commandsString), | ||
interfaceClassName: interfaceClassName | ||
}); | ||
files.set(`${outputDir}/${className}.java`, replacedTemplate); | ||
}); | ||
}); | ||
return files; | ||
} | ||
return files; | ||
}, | ||
}; | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,26 +10,21 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
const { | ||
getImports, | ||
toSafeJavaString, | ||
getInterfaceJavaClassName, | ||
} = require('./JavaHelpers'); | ||
const _require = require('./JavaHelpers'), | ||
getImports = _require.getImports, | ||
toSafeJavaString = _require.toSafeJavaString, | ||
getInterfaceJavaClassName = _require.getInterfaceJavaClassName; // File path -> contents | ||
// File path -> contents | ||
const template = `/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
const FileTemplate = ({ | ||
packageName, | ||
imports, | ||
className, | ||
extendClasses, | ||
methods | ||
}) => `/** | ||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* Do not edit this file as changes may cause incorrect behavior and will be lost | ||
* once the code is regenerated. | ||
* | ||
@@ -39,8 +34,8 @@ * ${'@'}generated by codegen project: GeneratePropsJavaInterface.js | ||
package ::_PACKAGE_NAME_::; | ||
package ${packageName}; | ||
::_IMPORTS_:: | ||
${imports} | ||
public interface ::_CLASSNAME_::<T extends ::_EXTEND_CLASSES_::> { | ||
::_METHODS_:: | ||
public interface ${className}<T extends ${extendClasses}> { | ||
${methods} | ||
} | ||
@@ -53,6 +48,3 @@ `; | ||
function getJavaValueForProp( | ||
prop , | ||
imports, | ||
) { | ||
function getJavaValueForProp(prop, imports) { | ||
const typeAnnotation = prop.typeAnnotation; | ||
@@ -68,9 +60,13 @@ | ||
} | ||
case 'StringTypeAnnotation': | ||
addNullable(imports); | ||
return '@Nullable String value'; | ||
case 'Int32TypeAnnotation': | ||
return 'int value'; | ||
case 'DoubleTypeAnnotation': | ||
return 'double value'; | ||
case 'FloatTypeAnnotation': | ||
@@ -83,2 +79,3 @@ if (typeAnnotation.default === null) { | ||
} | ||
case 'ReservedPropTypeAnnotation': | ||
@@ -89,31 +86,46 @@ switch (typeAnnotation.name) { | ||
return '@Nullable Integer value'; | ||
case 'ImageSourcePrimitive': | ||
addNullable(imports); | ||
return '@Nullable ReadableMap value'; | ||
case 'ImageRequestPrimitive': | ||
addNullable(imports); | ||
return '@Nullable ReadableMap value'; | ||
case 'PointPrimitive': | ||
addNullable(imports); | ||
return '@Nullable ReadableMap value'; | ||
case 'EdgeInsetsPrimitive': | ||
addNullable(imports); | ||
return '@Nullable ReadableMap value'; | ||
default: | ||
(typeAnnotation.name ); | ||
typeAnnotation.name; | ||
throw new Error('Received unknown ReservedPropTypeAnnotation'); | ||
} | ||
case 'ArrayTypeAnnotation': { | ||
addNullable(imports); | ||
return '@Nullable ReadableArray value'; | ||
} | ||
case 'ObjectTypeAnnotation': { | ||
addNullable(imports); | ||
return '@Nullable ReadableMap value'; | ||
} | ||
case 'ArrayTypeAnnotation': | ||
{ | ||
addNullable(imports); | ||
return '@Nullable ReadableArray value'; | ||
} | ||
case 'ObjectTypeAnnotation': | ||
{ | ||
addNullable(imports); | ||
return '@Nullable ReadableMap value'; | ||
} | ||
case 'StringEnumTypeAnnotation': | ||
addNullable(imports); | ||
return '@Nullable String value'; | ||
case 'Int32EnumTypeAnnotation': | ||
addNullable(imports); | ||
return '@Nullable Integer value'; | ||
default: | ||
(typeAnnotation ); | ||
typeAnnotation; | ||
throw new Error('Received invalid typeAnnotation'); | ||
@@ -123,3 +135,3 @@ } | ||
function generatePropsString(component , imports) { | ||
function generatePropsString(component, imports) { | ||
if (component.props.length === 0) { | ||
@@ -129,13 +141,9 @@ return '// No props'; | ||
return component.props | ||
.map(prop => { | ||
return `void set${toSafeJavaString( | ||
prop.name, | ||
)}(T view, ${getJavaValueForProp(prop, imports)});`; | ||
}) | ||
.join('\n' + ' '); | ||
return component.props.map(prop => { | ||
return `void set${toSafeJavaString(prop.name)}(T view, ${getJavaValueForProp(prop, imports)});`; | ||
}).join('\n' + ' '); | ||
} | ||
function getCommandArgJavaType(param) { | ||
const {typeAnnotation} = param; | ||
const typeAnnotation = param.typeAnnotation; | ||
@@ -147,18 +155,25 @@ switch (typeAnnotation.type) { | ||
return 'double'; | ||
default: | ||
(typeAnnotation.name ); | ||
typeAnnotation.name; | ||
throw new Error(`Receieved invalid type: ${typeAnnotation.name}`); | ||
} | ||
case 'BooleanTypeAnnotation': | ||
return 'boolean'; | ||
case 'DoubleTypeAnnotation': | ||
return 'double'; | ||
case 'FloatTypeAnnotation': | ||
return 'float'; | ||
case 'Int32TypeAnnotation': | ||
return 'int'; | ||
case 'StringTypeAnnotation': | ||
return 'String'; | ||
default: | ||
(typeAnnotation.type ); | ||
typeAnnotation.type; | ||
throw new Error('Receieved invalid typeAnnotation'); | ||
@@ -168,51 +183,34 @@ } | ||
function getCommandArguments( | ||
command , | ||
componentName , | ||
) { | ||
return [ | ||
'T view', | ||
...command.typeAnnotation.params.map(param => { | ||
const commandArgJavaType = getCommandArgJavaType(param); | ||
function getCommandArguments(command, componentName) { | ||
return ['T view', ...command.typeAnnotation.params.map(param => { | ||
const commandArgJavaType = getCommandArgJavaType(param); | ||
return `${commandArgJavaType} ${param.name}`; | ||
})].join(', '); | ||
} | ||
return `${commandArgJavaType} ${param.name}`; | ||
}), | ||
].join(', '); | ||
function generateCommandsString(component, componentName) { | ||
return component.commands.map(command => { | ||
const safeJavaName = toSafeJavaString(command.name, false); | ||
return `void ${safeJavaName}(${getCommandArguments(command, componentName)});`; | ||
}).join('\n' + ' '); | ||
} | ||
function generateCommandsString( | ||
component , | ||
componentName , | ||
) { | ||
return component.commands | ||
.map(command => { | ||
const safeJavaName = toSafeJavaString(command.name, false); | ||
function getClassExtendString(component) { | ||
const extendString = component.extendsProps.map(extendProps => { | ||
switch (extendProps.type) { | ||
case 'ReactNativeBuiltInType': | ||
switch (extendProps.knownTypeName) { | ||
case 'ReactNativeCoreViewProps': | ||
return 'View'; | ||
return `void ${safeJavaName}(${getCommandArguments( | ||
command, | ||
componentName, | ||
)});`; | ||
}) | ||
.join('\n' + ' '); | ||
} | ||
default: | ||
extendProps.knownTypeName; | ||
throw new Error('Invalid knownTypeName'); | ||
} | ||
function getClassExtendString(component) { | ||
const extendString = component.extendsProps | ||
.map(extendProps => { | ||
switch (extendProps.type) { | ||
case 'ReactNativeBuiltInType': | ||
switch (extendProps.knownTypeName) { | ||
case 'ReactNativeCoreViewProps': | ||
return 'View'; | ||
default: | ||
(extendProps.knownTypeName ); | ||
throw new Error('Invalid knownTypeName'); | ||
} | ||
default: | ||
(extendProps.type ); | ||
throw new Error('Invalid extended type'); | ||
} | ||
}) | ||
.join(''); | ||
default: | ||
extendProps.type; | ||
throw new Error('Invalid extended type'); | ||
} | ||
}).join(''); | ||
return extendString; | ||
@@ -222,15 +220,10 @@ } | ||
module.exports = { | ||
generate( | ||
libraryName , | ||
schema , | ||
packageName , | ||
assumeNonnull = false, | ||
) { | ||
generate(libraryName, schema, packageName, assumeNonnull = false) { | ||
// TODO: This doesn't support custom package name yet. | ||
const normalizedPackageName = 'com.facebook.react.viewmanagers'; | ||
const outputDir = `java/${normalizedPackageName.replace(/\./g, '/')}`; | ||
const files = new Map(); | ||
Object.keys(schema.modules).forEach(moduleName => { | ||
const module = schema.modules[moduleName]; | ||
if (module.type !== 'Component') { | ||
@@ -240,5 +233,4 @@ return; | ||
const {components} = module; | ||
const components = module.components; // No components in this module | ||
// No components in this module | ||
if (components == null) { | ||
@@ -248,44 +240,25 @@ return; | ||
return Object.keys(components) | ||
.filter(componentName => { | ||
const component = components[componentName]; | ||
return !( | ||
component.excludedPlatforms && | ||
component.excludedPlatforms.includes('android') | ||
); | ||
}) | ||
.forEach(componentName => { | ||
const component = components[componentName]; | ||
const className = getInterfaceJavaClassName(componentName); | ||
const imports = getImports(component, 'interface'); | ||
const propsString = generatePropsString(component, imports); | ||
const commandsString = generateCommandsString( | ||
component, | ||
componentName, | ||
); | ||
const extendString = getClassExtendString(component); | ||
const replacedTemplate = template | ||
.replace( | ||
/::_IMPORTS_::/g, | ||
Array.from(imports) | ||
.sort() | ||
.join('\n'), | ||
) | ||
.replace(/::_PACKAGE_NAME_::/g, normalizedPackageName) | ||
.replace(/::_CLASSNAME_::/g, className) | ||
.replace('::_EXTEND_CLASSES_::', extendString) | ||
.replace( | ||
'::_METHODS_::', | ||
[propsString, commandsString].join('\n' + ' ').trimRight(), | ||
) | ||
.replace('::_COMMAND_HANDLERS_::', commandsString); | ||
files.set(`${outputDir}/${className}.java`, replacedTemplate); | ||
return Object.keys(components).filter(componentName => { | ||
const component = components[componentName]; | ||
return !(component.excludedPlatforms && component.excludedPlatforms.includes('android')); | ||
}).forEach(componentName => { | ||
const component = components[componentName]; | ||
const className = getInterfaceJavaClassName(componentName); | ||
const imports = getImports(component, 'interface'); | ||
const propsString = generatePropsString(component, imports); | ||
const commandsString = generateCommandsString(component, componentName); | ||
const extendString = getClassExtendString(component); | ||
const replacedTemplate = FileTemplate({ | ||
imports: Array.from(imports).sort().join('\n'), | ||
packageName: normalizedPackageName, | ||
className, | ||
extendClasses: extendString, | ||
methods: [propsString, commandsString].join('\n' + ' ').trimRight() | ||
}); | ||
files.set(`${outputDir}/${className}.java`, replacedTemplate); | ||
}); | ||
}); | ||
return files; | ||
} | ||
return files; | ||
}, | ||
}; | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,24 +10,19 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
const PojoCollector = require('./PojoCollector'); | ||
const {capitalize} = require('../../Utils'); | ||
const {serializePojo} = require('./serializePojo'); | ||
const _require = require('../../Utils'), | ||
capitalize = _require.capitalize; | ||
const _require2 = require('./serializePojo'), | ||
serializePojo = _require2.serializePojo; | ||
module.exports = { | ||
generate( | ||
libraryName , | ||
schema , | ||
packageName , | ||
) { | ||
generate(libraryName, schema, packageName) { | ||
const pojoCollector = new PojoCollector(); | ||
const basePackageName = 'com.facebook.react.viewmanagers'; | ||
Object.keys(schema.modules).forEach(hasteModuleName => { | ||
const module = schema.modules[hasteModuleName]; | ||
if (module.type !== 'Component') { | ||
@@ -37,4 +32,4 @@ return; | ||
const {components} = module; | ||
// No components in this module | ||
const components = module.components; // No components in this module | ||
if (components == null) { | ||
@@ -44,40 +39,25 @@ return null; | ||
Object.keys(components) | ||
.filter(componentName => { | ||
const component = components[componentName]; | ||
return !( | ||
component.excludedPlatforms && | ||
component.excludedPlatforms.includes('android') | ||
); | ||
}) | ||
.forEach(componentName => { | ||
const component = components[componentName]; | ||
if (component == null) { | ||
return; | ||
} | ||
Object.keys(components).filter(componentName => { | ||
const component = components[componentName]; | ||
return !(component.excludedPlatforms && component.excludedPlatforms.includes('android')); | ||
}).forEach(componentName => { | ||
const component = components[componentName]; | ||
const {props} = component; | ||
if (component == null) { | ||
return; | ||
} | ||
pojoCollector.process( | ||
capitalize(hasteModuleName), | ||
`${capitalize(componentName)}Props`, | ||
{ | ||
type: 'ObjectTypeAnnotation', | ||
properties: props, | ||
}, | ||
); | ||
const props = component.props; | ||
pojoCollector.process(capitalize(hasteModuleName), `${capitalize(componentName)}Props`, { | ||
type: 'ObjectTypeAnnotation', | ||
properties: props | ||
}); | ||
}); | ||
}); | ||
const pojoDir = basePackageName.split('.').join('/'); | ||
return new Map(pojoCollector.getAllPojos().map(pojo => { | ||
return [`java/${pojoDir}/${pojo.namespace}/${pojo.name}.java`, serializePojo(pojo, basePackageName)]; | ||
})); | ||
} | ||
return new Map( | ||
pojoCollector.getAllPojos().map(pojo => { | ||
return [ | ||
`java/${pojoDir}/${pojo.namespace}/${pojo.name}.java`, | ||
serializePojo(pojo, basePackageName), | ||
]; | ||
}), | ||
); | ||
}, | ||
}; | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,144 +10,76 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } | ||
const {capitalize} = require('../../Utils'); | ||
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
const _require = require('../../Utils'), | ||
capitalize = _require.capitalize; | ||
class PojoCollector { | ||
constructor() { | ||
_defineProperty(this, "_pojos", new Map()); | ||
} | ||
class PojoCollector { | ||
_pojos = new Map(); | ||
process( | ||
namespace , | ||
pojoName , | ||
typeAnnotation , | ||
) { | ||
process(namespace, pojoName, typeAnnotation) { | ||
switch (typeAnnotation.type) { | ||
case 'ObjectTypeAnnotation': { | ||
this._insertPojo(namespace, pojoName, typeAnnotation); | ||
return { | ||
type: 'PojoTypeAliasTypeAnnotation', | ||
name: pojoName, | ||
}; | ||
} | ||
case 'ArrayTypeAnnotation': { | ||
const arrayTypeAnnotation = typeAnnotation; | ||
// TODO: Flow assumes elementType can be any. Fix this. | ||
const elementType | ||
= arrayTypeAnnotation.elementType; | ||
case 'ObjectTypeAnnotation': | ||
{ | ||
this._insertPojo(namespace, pojoName, typeAnnotation); | ||
const pojoElementType = (() => { | ||
switch (elementType.type) { | ||
case 'ObjectTypeAnnotation': { | ||
this._insertPojo(namespace, `${pojoName}Element`, elementType); | ||
return { | ||
type: 'PojoTypeAliasTypeAnnotation', | ||
name: `${pojoName}Element`, | ||
}; | ||
return { | ||
type: 'PojoTypeAliasTypeAnnotation', | ||
name: pojoName | ||
}; | ||
} | ||
case 'ArrayTypeAnnotation': | ||
{ | ||
const arrayTypeAnnotation = typeAnnotation; // TODO: Flow assumes elementType can be any. Fix this. | ||
const elementType = arrayTypeAnnotation.elementType; | ||
const pojoElementType = (() => { | ||
switch (elementType.type) { | ||
case 'ObjectTypeAnnotation': | ||
{ | ||
this._insertPojo(namespace, `${pojoName}Element`, elementType); | ||
return { | ||
type: 'PojoTypeAliasTypeAnnotation', | ||
name: `${pojoName}Element` | ||
}; | ||
} | ||
case 'ArrayTypeAnnotation': | ||
{ | ||
const objectTypeAnnotation = elementType.elementType; | ||
this._insertPojo(namespace, `${pojoName}ElementElement`, objectTypeAnnotation); | ||
return { | ||
type: 'ArrayTypeAnnotation', | ||
elementType: { | ||
type: 'PojoTypeAliasTypeAnnotation', | ||
name: `${pojoName}ElementElement` | ||
} | ||
}; | ||
} | ||
default: | ||
{ | ||
return elementType; | ||
} | ||
} | ||
case 'ArrayTypeAnnotation': { | ||
const {elementType: objectTypeAnnotation} = elementType; | ||
this._insertPojo( | ||
namespace, | ||
`${pojoName}ElementElement`, | ||
objectTypeAnnotation, | ||
); | ||
return { | ||
type: 'ArrayTypeAnnotation', | ||
elementType: { | ||
type: 'PojoTypeAliasTypeAnnotation', | ||
name: `${pojoName}ElementElement`, | ||
}, | ||
}; | ||
} | ||
default: { | ||
return elementType; | ||
} | ||
} | ||
})(); | ||
})(); | ||
return { | ||
type: 'ArrayTypeAnnotation', | ||
elementType: pojoElementType, | ||
}; | ||
} | ||
return { | ||
type: 'ArrayTypeAnnotation', | ||
elementType: pojoElementType | ||
}; | ||
} | ||
default: | ||
@@ -158,18 +90,8 @@ return typeAnnotation; | ||
_insertPojo( | ||
namespace , | ||
pojoName , | ||
objectTypeAnnotation , | ||
) { | ||
_insertPojo(namespace, pojoName, objectTypeAnnotation) { | ||
const properties = objectTypeAnnotation.properties.map(property => { | ||
const propertyPojoName = pojoName + capitalize(property.name); | ||
return { | ||
...property, | ||
typeAnnotation: this.process( | ||
namespace, | ||
propertyPojoName, | ||
property.typeAnnotation, | ||
), | ||
}; | ||
return _objectSpread({}, property, { | ||
typeAnnotation: this.process(namespace, propertyPojoName, property.typeAnnotation) | ||
}); | ||
}); | ||
@@ -180,11 +102,12 @@ | ||
namespace, | ||
properties, | ||
properties | ||
}); | ||
} | ||
getAllPojos() { | ||
getAllPojos() { | ||
return [...this._pojos.values()]; | ||
} | ||
} | ||
module.exports = PojoCollector; | ||
module.exports = PojoCollector; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,18 +10,14 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
const {capitalize} = require('../../Utils'); | ||
const _require = require('../../Utils'), | ||
capitalize = _require.capitalize; | ||
function toJavaType(typeAnnotation, addImport) { | ||
const importNullable = () => addImport('androidx.annotation.Nullable'); | ||
function toJavaType( | ||
typeAnnotation , | ||
addImport , | ||
) { | ||
const importNullable = () => addImport('androidx.annotation.Nullable'); | ||
const importReadableMap = () => | ||
addImport('com.facebook.react.bridge.ReadableMap'); | ||
const importReadableMap = () => addImport('com.facebook.react.bridge.ReadableMap'); | ||
const importArrayList = () => addImport('java.util.ArrayList'); | ||
switch (typeAnnotation.type) { | ||
@@ -31,29 +27,38 @@ /** | ||
*/ | ||
case 'BooleanTypeAnnotation': { | ||
if (typeAnnotation.default === null) { | ||
importNullable(); | ||
return '@Nullable Boolean'; | ||
} else { | ||
return 'boolean'; | ||
case 'BooleanTypeAnnotation': | ||
{ | ||
if (typeAnnotation.default === null) { | ||
importNullable(); | ||
return '@Nullable Boolean'; | ||
} else { | ||
return 'boolean'; | ||
} | ||
} | ||
} | ||
case 'StringTypeAnnotation': { | ||
importNullable(); | ||
return '@Nullable String'; | ||
} | ||
case 'DoubleTypeAnnotation': { | ||
return 'double'; | ||
} | ||
case 'FloatTypeAnnotation': { | ||
if (typeAnnotation.default === null) { | ||
case 'StringTypeAnnotation': | ||
{ | ||
importNullable(); | ||
return '@Nullable Float'; | ||
} else { | ||
return 'float'; | ||
return '@Nullable String'; | ||
} | ||
} | ||
case 'Int32TypeAnnotation': { | ||
return 'int'; | ||
} | ||
case 'DoubleTypeAnnotation': | ||
{ | ||
return 'double'; | ||
} | ||
case 'FloatTypeAnnotation': | ||
{ | ||
if (typeAnnotation.default === null) { | ||
importNullable(); | ||
return '@Nullable Float'; | ||
} else { | ||
return 'float'; | ||
} | ||
} | ||
case 'Int32TypeAnnotation': | ||
{ | ||
return 'int'; | ||
} | ||
/** | ||
@@ -63,2 +68,3 @@ * Enums | ||
// TODO: Make StringEnumTypeAnnotation type-safe? | ||
case 'StringEnumTypeAnnotation': | ||
@@ -68,2 +74,3 @@ importNullable(); | ||
// TODO: Make Int32EnumTypeAnnotation type-safe? | ||
case 'Int32EnumTypeAnnotation': | ||
@@ -76,32 +83,39 @@ importNullable(); | ||
*/ | ||
case 'ReservedPropTypeAnnotation': { | ||
switch (typeAnnotation.name) { | ||
case 'ColorPrimitive': | ||
importNullable(); | ||
return '@Nullable Integer'; | ||
// TODO: Make ImageSourcePrimitive type-safe | ||
case 'ImageSourcePrimitive': | ||
importNullable(); | ||
importReadableMap(); | ||
return '@Nullable ReadableMap'; | ||
case 'ReservedPropTypeAnnotation': | ||
{ | ||
switch (typeAnnotation.name) { | ||
case 'ColorPrimitive': | ||
importNullable(); | ||
return '@Nullable Integer'; | ||
// TODO: Make ImageSourcePrimitive type-safe | ||
// TODO: Make PointPrimitive type-safe | ||
case 'PointPrimitive': | ||
importNullable(); | ||
importReadableMap(); | ||
return '@Nullable ReadableMap'; | ||
case 'ImageSourcePrimitive': | ||
importNullable(); | ||
importReadableMap(); | ||
return '@Nullable ReadableMap'; | ||
// TODO: Make ImageRequestPrimitive type-safe | ||
// TODO: Make EdgeInsetsPrimitive type-safe | ||
case 'EdgeInsetsPrimitive': | ||
importNullable(); | ||
importReadableMap(); | ||
return '@Nullable ReadableMap'; | ||
default: | ||
(typeAnnotation.name ); | ||
throw new Error( | ||
`Received unknown ReservedPropTypeAnnotation ${typeAnnotation.name}`, | ||
); | ||
case 'ImageRequestPrimitive': | ||
importNullable(); | ||
importReadableMap(); | ||
return '@Nullable ReadableMap'; | ||
// TODO: Make PointPrimitive type-safe | ||
case 'PointPrimitive': | ||
importNullable(); | ||
importReadableMap(); | ||
return '@Nullable ReadableMap'; | ||
// TODO: Make EdgeInsetsPrimitive type-safe | ||
case 'EdgeInsetsPrimitive': | ||
importNullable(); | ||
importReadableMap(); | ||
return '@Nullable ReadableMap'; | ||
default: | ||
typeAnnotation.name; | ||
throw new Error(`Received unknown ReservedPropTypeAnnotation ${typeAnnotation.name}`); | ||
} | ||
} | ||
} | ||
@@ -111,115 +125,134 @@ /** | ||
*/ | ||
case 'PojoTypeAliasTypeAnnotation': { | ||
return typeAnnotation.name; | ||
} | ||
case 'PojoTypeAliasTypeAnnotation': | ||
{ | ||
return typeAnnotation.name; | ||
} | ||
/** | ||
* Arrays | ||
*/ | ||
case 'ArrayTypeAnnotation': { | ||
const {elementType} = typeAnnotation; | ||
const elementTypeString = (() => { | ||
switch (elementType.type) { | ||
/** | ||
* Primitives | ||
*/ | ||
case 'BooleanTypeAnnotation': { | ||
return 'Boolean'; | ||
} | ||
case 'StringTypeAnnotation': { | ||
return 'String'; | ||
} | ||
case 'DoubleTypeAnnotation': { | ||
return 'Double'; | ||
} | ||
case 'FloatTypeAnnotation': { | ||
return 'Float'; | ||
} | ||
case 'Int32TypeAnnotation': { | ||
return 'Integer'; | ||
} | ||
case 'ArrayTypeAnnotation': | ||
{ | ||
const elementType = typeAnnotation.elementType; | ||
/** | ||
* Enums | ||
*/ | ||
// TODO: Make StringEnums type-safe in Pojos | ||
case 'StringEnumTypeAnnotation': { | ||
return 'String'; | ||
} | ||
const elementTypeString = (() => { | ||
switch (elementType.type) { | ||
/** | ||
* Primitives | ||
*/ | ||
case 'BooleanTypeAnnotation': | ||
{ | ||
return 'Boolean'; | ||
} | ||
/** | ||
* Other Pojo objects | ||
*/ | ||
case 'PojoTypeAliasTypeAnnotation': { | ||
return elementType.name; | ||
} | ||
case 'StringTypeAnnotation': | ||
{ | ||
return 'String'; | ||
} | ||
/** | ||
* Reserved types | ||
*/ | ||
case 'ReservedPropTypeAnnotation': { | ||
switch (elementType.name) { | ||
case 'ColorPrimitive': | ||
case 'DoubleTypeAnnotation': | ||
{ | ||
return 'Double'; | ||
} | ||
case 'FloatTypeAnnotation': | ||
{ | ||
return 'Float'; | ||
} | ||
case 'Int32TypeAnnotation': | ||
{ | ||
return 'Integer'; | ||
} | ||
// TODO: Make ImageSourcePrimitive type-safe | ||
case 'ImageSourcePrimitive': | ||
importReadableMap(); | ||
return 'ReadableMap'; | ||
/** | ||
* Enums | ||
*/ | ||
// TODO: Make StringEnums type-safe in Pojos | ||
// TODO: Make PointPrimitive type-safe | ||
case 'PointPrimitive': | ||
importReadableMap(); | ||
return 'ReadableMap'; | ||
case 'StringEnumTypeAnnotation': | ||
{ | ||
return 'String'; | ||
} | ||
// TODO: Make EdgeInsetsPrimitive type-safe | ||
case 'EdgeInsetsPrimitive': | ||
importReadableMap(); | ||
return 'ReadableMap'; | ||
default: | ||
(elementType.name ); | ||
throw new Error( | ||
`Received unknown ReservedPropTypeAnnotation ${elementType.name}`, | ||
); | ||
} | ||
} | ||
/** | ||
* Other Pojo objects | ||
*/ | ||
// Arrays | ||
case 'ArrayTypeAnnotation': { | ||
const {elementType: pojoTypeAliasTypeAnnotation} = elementType; | ||
case 'PojoTypeAliasTypeAnnotation': | ||
{ | ||
return elementType.name; | ||
} | ||
importArrayList(); | ||
return `ArrayList<${pojoTypeAliasTypeAnnotation.name}>`; | ||
/** | ||
* Reserved types | ||
*/ | ||
case 'ReservedPropTypeAnnotation': | ||
{ | ||
switch (elementType.name) { | ||
case 'ColorPrimitive': | ||
return 'Integer'; | ||
// TODO: Make ImageSourcePrimitive type-safe | ||
case 'ImageSourcePrimitive': | ||
importReadableMap(); | ||
return 'ReadableMap'; | ||
// TODO: Make ImageRequestPrimitive type-safe | ||
case 'ImageRequestPrimitive': | ||
importReadableMap(); | ||
return 'ReadableMap'; | ||
// TODO: Make PointPrimitive type-safe | ||
case 'PointPrimitive': | ||
importReadableMap(); | ||
return 'ReadableMap'; | ||
// TODO: Make EdgeInsetsPrimitive type-safe | ||
case 'EdgeInsetsPrimitive': | ||
importReadableMap(); | ||
return 'ReadableMap'; | ||
default: | ||
elementType.name; | ||
throw new Error(`Received unknown ReservedPropTypeAnnotation ${elementType.name}`); | ||
} | ||
} | ||
// Arrays | ||
case 'ArrayTypeAnnotation': | ||
{ | ||
const pojoTypeAliasTypeAnnotation = elementType.elementType; | ||
importArrayList(); | ||
return `ArrayList<${pojoTypeAliasTypeAnnotation.name}>`; | ||
} | ||
default: | ||
{ | ||
elementType.type; | ||
throw new Error(`Unrecognized PojoTypeAnnotation Array element type annotation '${typeAnnotation.type}'`); | ||
} | ||
} | ||
default: { | ||
(elementType.type ); | ||
throw new Error( | ||
`Unrecognized PojoTypeAnnotation Array element type annotation '${typeAnnotation.type}'`, | ||
); | ||
} | ||
} | ||
})(); | ||
})(); | ||
importArrayList(); | ||
return `ArrayList<${elementTypeString}>`; | ||
} | ||
importArrayList(); | ||
return `ArrayList<${elementTypeString}>`; | ||
} | ||
default: { | ||
(typeAnnotation.type ); | ||
throw new Error( | ||
`Unrecognized PojoTypeAnnotation '${typeAnnotation.type}'`, | ||
); | ||
} | ||
default: | ||
{ | ||
typeAnnotation.type; | ||
throw new Error(`Unrecognized PojoTypeAnnotation '${typeAnnotation.type}'`); | ||
} | ||
} | ||
} | ||
function toJavaMemberName(property ) { | ||
function toJavaMemberName(property) { | ||
return `m${capitalize(property.name)}`; | ||
} | ||
function toJavaMemberDeclaration( | ||
property , | ||
addImport , | ||
) { | ||
function toJavaMemberDeclaration(property, addImport) { | ||
const type = toJavaType(property.typeAnnotation, addImport); | ||
@@ -230,7 +263,6 @@ const memberName = toJavaMemberName(property); | ||
function toJavaGetter(property , addImport ) { | ||
function toJavaGetter(property, addImport) { | ||
const type = toJavaType(property.typeAnnotation, addImport); | ||
const getterName = `get${capitalize(property.name)}`; | ||
const memberName = toJavaMemberName(property); | ||
addImport('com.facebook.proguard.annotations.DoNotStrip'); | ||
@@ -243,5 +275,6 @@ return `@DoNotStrip | ||
function serializePojo(pojo , basePackageName ) { | ||
const importSet = new Set(); | ||
const addImport = ($import ) => { | ||
function serializePojo(pojo, basePackageName) { | ||
const importSet = new Set(); | ||
const addImport = $import => { | ||
importSet.add($import); | ||
@@ -251,27 +284,8 @@ }; | ||
addImport('com.facebook.proguard.annotations.DoNotStrip'); | ||
const indent = ' '.repeat(2); | ||
const members = pojo.properties | ||
.map(property => toJavaMemberDeclaration(property, addImport)) | ||
.map(member => `${indent}${member}`) | ||
.join('\n'); | ||
const getters = pojo.properties | ||
.map(property => toJavaGetter(property, addImport)) | ||
.map(getter => | ||
getter | ||
.split('\n') | ||
.map(line => `${indent}${line}`) | ||
.join('\n'), | ||
) | ||
.join('\n'); | ||
const imports = [...importSet] | ||
.map($import => `import ${$import};`) | ||
.sort() | ||
.join('\n'); | ||
const members = pojo.properties.map(property => toJavaMemberDeclaration(property, addImport)).map(member => `${indent}${member}`).join('\n'); | ||
const getters = pojo.properties.map(property => toJavaGetter(property, addImport)).map(getter => getter.split('\n').map(line => `${indent}${line}`).join('\n')).join('\n'); | ||
const imports = [...importSet].map($import => `import ${$import};`).sort().join('\n'); | ||
return `/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -294,2 +308,4 @@ * This source code is licensed under the MIT license found in the | ||
module.exports = {serializePojo}; | ||
module.exports = { | ||
serializePojo | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,16 +10,13 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; // File path -> contents | ||
'use strict'; | ||
// File path -> contents | ||
const template = ` | ||
const FileTemplate = ({ | ||
libraryName, | ||
componentNames | ||
}) => ` | ||
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* Do not edit this file as changes may cause incorrect behavior and will be lost | ||
* once the code is regenerated. | ||
* | ||
@@ -29,3 +26,3 @@ * ${'@'}generated by codegen project: GenerateShadowNodeCpp.js | ||
#include <react/renderer/components/::_LIBRARY_::/ShadowNodes.h> | ||
#include <react/renderer/components/${libraryName}/ShadowNodes.h> | ||
@@ -35,3 +32,3 @@ namespace facebook { | ||
::_COMPONENT_NAMES_:: | ||
${componentNames} | ||
@@ -42,51 +39,42 @@ } // namespace react | ||
const componentTemplate = ` | ||
extern const char ::_CLASSNAME_::ComponentName[] = "::_CLASSNAME_::"; | ||
const ComponentTemplate = ({ | ||
className | ||
}) => ` | ||
extern const char ${className}ComponentName[] = "${className}"; | ||
`.trim(); | ||
module.exports = { | ||
generate( | ||
libraryName , | ||
schema , | ||
packageName , | ||
assumeNonnull = false, | ||
) { | ||
generate(libraryName, schema, packageName, assumeNonnull = false) { | ||
const fileName = 'ShadowNodes.cpp'; | ||
const componentNames = Object.keys(schema.modules).map(moduleName => { | ||
const module = schema.modules[moduleName]; | ||
const componentNames = Object.keys(schema.modules) | ||
.map(moduleName => { | ||
const module = schema.modules[moduleName]; | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
const {components} = module; | ||
// No components in this module | ||
if (components == null) { | ||
return null; | ||
} | ||
const components = module.components; // No components in this module | ||
return Object.keys(components) | ||
.map(componentName => { | ||
if (components[componentName].interfaceOnly === true) { | ||
return; | ||
} | ||
const replacedTemplate = componentTemplate.replace( | ||
/::_CLASSNAME_::/g, | ||
componentName, | ||
); | ||
if (components == null) { | ||
return null; | ||
} | ||
return replacedTemplate; | ||
}) | ||
.join('\n'); | ||
}) | ||
.filter(Boolean) | ||
.join('\n'); | ||
return Object.keys(components).map(componentName => { | ||
if (components[componentName].interfaceOnly === true) { | ||
return; | ||
} | ||
const replacedTemplate = template | ||
.replace(/::_COMPONENT_NAMES_::/g, componentNames) | ||
.replace('::_LIBRARY_::', libraryName); | ||
const replacedTemplate = ComponentTemplate({ | ||
className: componentName | ||
}); | ||
return replacedTemplate; | ||
}).join('\n'); | ||
}).filter(Boolean).join('\n'); | ||
const replacedTemplate = FileTemplate({ | ||
componentNames, | ||
libraryName | ||
}); | ||
return new Map([[fileName, replacedTemplate]]); | ||
} | ||
return new Map([[fileName, replacedTemplate]]); | ||
}, | ||
}; | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,16 +10,14 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; // File path -> contents | ||
'use strict'; | ||
// File path -> contents | ||
const template = ` | ||
const FileTemplate = ({ | ||
imports, | ||
libraryName, | ||
componentClasses | ||
}) => ` | ||
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* Do not edit this file as changes may cause incorrect behavior and will be lost | ||
* once the code is regenerated. | ||
* | ||
@@ -31,4 +29,6 @@ * ${'@'}generated by codegen project: GenerateShadowNodeH.js | ||
::_IMPORTS_::#include <react/renderer/components/::_LIBRARY_::/Props.h> | ||
${imports}#include <react/renderer/components/${libraryName}/Props.h> | ||
#include <react/renderer/components/${libraryName}/States.h> | ||
#include <react/renderer/components/view/ConcreteViewShadowNode.h> | ||
#include <jsi/jsi.h> | ||
@@ -38,3 +38,3 @@ namespace facebook { | ||
::_COMPONENT_CLASSES_:: | ||
${componentClasses} | ||
@@ -45,74 +45,57 @@ } // namespace react | ||
const componentTemplate = ` | ||
extern const char ::_CLASSNAME_::ComponentName[]; | ||
const ComponentTemplate = ({ | ||
className, | ||
eventEmitter | ||
}) => ` | ||
JSI_EXPORT extern const char ${className}ComponentName[]; | ||
/* | ||
* \`ShadowNode\` for <::_CLASSNAME_::> component. | ||
* \`ShadowNode\` for <${className}> component. | ||
*/ | ||
using ::_CLASSNAME_::ShadowNode = ConcreteViewShadowNode< | ||
::_CLASSNAME_::ComponentName, | ||
::_CLASSNAME_::Props::_EVENT_EMITTER_::>; | ||
using ${className}ShadowNode = ConcreteViewShadowNode< | ||
${className}ComponentName, | ||
${className}Props${eventEmitter}, | ||
${className}State>; | ||
`.trim(); | ||
module.exports = { | ||
generate( | ||
libraryName , | ||
schema , | ||
packageName , | ||
assumeNonnull = false, | ||
) { | ||
generate(libraryName, schema, packageName, assumeNonnull = false) { | ||
const fileName = 'ShadowNodes.h'; | ||
const moduleResults = Object.keys(schema.modules).map(moduleName => { | ||
const module = schema.modules[moduleName]; | ||
let hasAnyEvents = false; | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
const moduleResults = Object.keys(schema.modules) | ||
.map(moduleName => { | ||
const module = schema.modules[moduleName]; | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
const components = module.components; // No components in this module | ||
const {components} = module; | ||
// No components in this module | ||
if (components == null) { | ||
return null; | ||
} | ||
if (components == null) { | ||
return null; | ||
} | ||
return Object.keys(components) | ||
.map(componentName => { | ||
const component = components[componentName]; | ||
if (component.interfaceOnly === true) { | ||
return; | ||
} | ||
return Object.keys(components).map(componentName => { | ||
const component = components[componentName]; | ||
const hasEvents = component.events.length > 0; | ||
if (component.interfaceOnly === true) { | ||
return; | ||
} | ||
if (hasEvents) { | ||
hasAnyEvents = true; | ||
} | ||
const eventEmitter = hasEvents | ||
? `,\n${componentName}EventEmitter` | ||
: ''; | ||
const replacedTemplate = componentTemplate | ||
.replace(/::_CLASSNAME_::/g, componentName) | ||
.replace('::_EVENT_EMITTER_::', eventEmitter); | ||
return replacedTemplate; | ||
}) | ||
.join('\n\n'); | ||
}) | ||
.filter(Boolean) | ||
.join('\n\n'); | ||
const eventEmitter = `,\n ${componentName}EventEmitter`; | ||
const replacedTemplate = ComponentTemplate({ | ||
className: componentName, | ||
eventEmitter | ||
}); | ||
return replacedTemplate; | ||
}).join('\n\n'); | ||
}).filter(Boolean).join('\n\n'); | ||
const eventEmitterImport = `#include <react/renderer/components/${libraryName}/EventEmitters.h>\n`; | ||
const replacedTemplate = FileTemplate({ | ||
componentClasses: moduleResults, | ||
libraryName, | ||
imports: eventEmitterImport | ||
}); | ||
return new Map([[fileName, replacedTemplate]]); | ||
} | ||
const replacedTemplate = template | ||
.replace(/::_COMPONENT_CLASSES_::/g, moduleResults) | ||
.replace('::_LIBRARY_::', libraryName) | ||
.replace('::_IMPORTS_::', hasAnyEvents ? eventEmitterImport : ''); | ||
return new Map([[fileName, replacedTemplate]]); | ||
}, | ||
}; | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,24 +10,18 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
const {getImports, toSafeCppString} = require('./CppHelpers'); | ||
const _require = require('./CppHelpers'), | ||
getImports = _require.getImports, | ||
toSafeCppString = _require.toSafeCppString; | ||
const fileTemplate = ` | ||
const FileTemplate = ({ | ||
libraryName, | ||
imports, | ||
componentTests | ||
}) => ` | ||
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* Do not edit this file as changes may cause incorrect behavior and will be lost | ||
* once the code is regenerated. | ||
* | ||
@@ -39,15 +33,20 @@ * ${'@'}generated by codegen project: GenerateTests.js | ||
#include <react/renderer/core/PropsParserContext.h> | ||
#include <react/renderer/components/::_LIBRARY_NAME_::/Props.h> | ||
::_IMPORTS_:: | ||
#include <react/renderer/components/${libraryName}/Props.h> | ||
${imports} | ||
using namespace facebook::react; | ||
::_COMPONENT_TESTS_:: | ||
`; | ||
${componentTests} | ||
`.trim(); | ||
const testTemplate = ` | ||
TEST(::_COMPONENT_NAME_::_::_TEST_NAME_::, etc) { | ||
const TestTemplate = ({ | ||
componentName, | ||
testName, | ||
propName, | ||
propValue | ||
}) => ` | ||
TEST(${componentName}_${testName}, etc) { | ||
auto propParser = RawPropsParser(); | ||
propParser.prepare<::_COMPONENT_NAME_::>(); | ||
auto const &sourceProps = ::_COMPONENT_NAME_::(); | ||
auto const &rawProps = RawProps(folly::dynamic::object("::_PROP_NAME_::", ::_PROP_VALUE_::)); | ||
propParser.prepare<${componentName}>(); | ||
auto const &sourceProps = ${componentName}(); | ||
auto const &rawProps = RawProps(folly::dynamic::object("${propName}", ${propValue})); | ||
@@ -58,3 +57,3 @@ ContextContainer contextContainer{}; | ||
rawProps.parse(propParser, parserContext); | ||
::_COMPONENT_NAME_::(parserContext, sourceProps, rawProps); | ||
${componentName}(parserContext, sourceProps, rawProps); | ||
} | ||
@@ -65,17 +64,13 @@ `; | ||
const cases = []; | ||
if (typeAnnotation.type === 'StringEnumTypeAnnotation') { | ||
typeAnnotation.options.forEach(option => | ||
cases.push({ | ||
propName, | ||
testName: `${propName}_${toSafeCppString(option)}`, | ||
propValue: option, | ||
}), | ||
); | ||
typeAnnotation.options.forEach(option => cases.push({ | ||
propName, | ||
testName: `${propName}_${toSafeCppString(option)}`, | ||
propValue: option | ||
})); | ||
} else if (typeAnnotation.type === 'StringTypeAnnotation') { | ||
cases.push({ | ||
propName, | ||
propValue: | ||
typeAnnotation.default != null && typeAnnotation.default !== '' | ||
? typeAnnotation.default | ||
: 'foo', | ||
propValue: typeAnnotation.default != null && typeAnnotation.default !== '' ? typeAnnotation.default : 'foo' | ||
}); | ||
@@ -85,9 +80,8 @@ } else if (typeAnnotation.type === 'BooleanTypeAnnotation') { | ||
propName: propName, | ||
propValue: typeAnnotation.default != null ? typeAnnotation.default : true, | ||
}); | ||
// $FlowFixMe[incompatible-type] | ||
propValue: typeAnnotation.default != null ? typeAnnotation.default : true | ||
}); // $FlowFixMe[incompatible-type] | ||
} else if (typeAnnotation.type === 'IntegerTypeAnnotation') { | ||
cases.push({ | ||
propName, | ||
propValue: typeAnnotation.default || 10, | ||
propValue: typeAnnotation.default || 10 | ||
}); | ||
@@ -97,3 +91,3 @@ } else if (typeAnnotation.type === 'FloatTypeAnnotation') { | ||
propName, | ||
propValue: typeAnnotation.default != null ? typeAnnotation.default : 0.1, | ||
propValue: typeAnnotation.default != null ? typeAnnotation.default : 0.1 | ||
}); | ||
@@ -104,3 +98,3 @@ } else if (typeAnnotation.type === 'ReservedPropTypeAnnotation') { | ||
propName, | ||
propValue: 1, | ||
propValue: 1 | ||
}); | ||
@@ -111,3 +105,3 @@ } else if (typeAnnotation.name === 'PointPrimitive') { | ||
propValue: 'folly::dynamic::object("x", 1)("y", 1)', | ||
raw: true, | ||
raw: true | ||
}); | ||
@@ -118,3 +112,3 @@ } else if (typeAnnotation.name === 'ImageSourcePrimitive') { | ||
propValue: 'folly::dynamic::object("url", "testurl")', | ||
raw: true, | ||
raw: true | ||
}); | ||
@@ -128,11 +122,15 @@ } | ||
function generateTestsString(name, component) { | ||
function createTest({testName, propName, propValue, raw = false} ) { | ||
const value = | ||
!raw && typeof propValue === 'string' ? `"${propValue}"` : propValue; | ||
return testTemplate | ||
.replace(/::_COMPONENT_NAME_::/g, name) | ||
.replace(/::_TEST_NAME_::/g, testName != null ? testName : propName) | ||
.replace(/::_PROP_NAME_::/g, propName) | ||
.replace(/::_PROP_VALUE_::/g, String(value)); | ||
function createTest({ | ||
testName, | ||
propName, | ||
propValue, | ||
raw = false | ||
}) { | ||
const value = !raw && typeof propValue === 'string' ? `"${propValue}"` : propValue; | ||
return TestTemplate({ | ||
componentName: name, | ||
testName: testName != null ? testName : propName, | ||
propName, | ||
propValue: String(value) | ||
}); | ||
} | ||
@@ -143,9 +141,7 @@ | ||
}, []); | ||
const baseTest = { | ||
testName: 'DoesNotDie', | ||
propName: 'xx_invalid_xx', | ||
propValue: 'xx_invalid_xx', | ||
propValue: 'xx_invalid_xx' | ||
}; | ||
return [baseTest, ...testCases].map(createTest).join(''); | ||
@@ -155,56 +151,36 @@ } | ||
module.exports = { | ||
generate( | ||
libraryName , | ||
schema , | ||
packageName , | ||
assumeNonnull = false, | ||
) { | ||
generate(libraryName, schema, packageName, assumeNonnull = false) { | ||
const fileName = 'Tests.cpp'; | ||
const allImports = new Set([ | ||
'#include <react/renderer/core/propsConversions.h>', | ||
'#include <react/renderer/core/RawProps.h>', | ||
'#include <react/renderer/core/RawPropsParser.h>', | ||
]); | ||
const allImports = new Set(['#include <react/renderer/core/propsConversions.h>', '#include <react/renderer/core/RawProps.h>', '#include <react/renderer/core/RawPropsParser.h>']); | ||
const componentTests = Object.keys(schema.modules).map(moduleName => { | ||
const module = schema.modules[moduleName]; | ||
const componentTests = Object.keys(schema.modules) | ||
.map(moduleName => { | ||
const module = schema.modules[moduleName]; | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
const {components} = module; | ||
if (components == null) { | ||
return null; | ||
} | ||
const components = module.components; | ||
return Object.keys(components) | ||
.map(componentName => { | ||
const component = components[componentName]; | ||
const name = `${componentName}Props`; | ||
if (components == null) { | ||
return null; | ||
} | ||
const imports = getImports(component.props); | ||
// $FlowFixMe[method-unbinding] added when improving typing for this parameters | ||
imports.forEach(allImports.add, allImports); | ||
return Object.keys(components).map(componentName => { | ||
const component = components[componentName]; | ||
const name = `${componentName}Props`; | ||
const imports = getImports(component.props); // $FlowFixMe[method-unbinding] added when improving typing for this parameters | ||
return generateTestsString(name, component); | ||
}) | ||
.join(''); | ||
}) | ||
.filter(Boolean) | ||
.join(''); | ||
imports.forEach(allImports.add, allImports); | ||
return generateTestsString(name, component); | ||
}).join(''); | ||
}).filter(Boolean).join(''); | ||
const imports = Array.from(allImports).sort().join('\n').trim(); | ||
const replacedTemplate = FileTemplate({ | ||
imports, | ||
libraryName, | ||
componentTests | ||
}); | ||
return new Map([[fileName, replacedTemplate]]); | ||
} | ||
const imports = Array.from(allImports) | ||
.sort() | ||
.join('\n') | ||
.trim(); | ||
const replacedTemplate = fileTemplate | ||
.replace(/::_IMPORTS_::/g, imports) | ||
.replace(/::_LIBRARY_NAME_::/g, libraryName) | ||
.replace(/::_COMPONENT_TESTS_::/g, componentTests) | ||
.trim(); | ||
return new Map([[fileName, replacedTemplate]]); | ||
}, | ||
}; | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,18 +10,16 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
const j = require('jscodeshift'); | ||
const j = require('jscodeshift'); // File path -> contents | ||
// File path -> contents | ||
const template = ` | ||
const FileTemplate = ({ | ||
imports, | ||
componentConfig | ||
}) => ` | ||
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* Do not edit this file as changes may cause incorrect behavior and will be lost | ||
* once the code is regenerated. | ||
* | ||
@@ -35,9 +33,9 @@ * @flow | ||
::_IMPORTS_:: | ||
${imports} | ||
::_COMPONENT_CONFIG_:: | ||
`; | ||
${componentConfig} | ||
`; // We use this to add to a set. Need to make sure we aren't importing | ||
// this multiple times. | ||
// We use this to add to a set. Need to make sure we aren't importing | ||
// this multiple times. | ||
const UIMANAGER_IMPORT = 'const {UIManager} = require("react-native")'; | ||
@@ -56,19 +54,25 @@ | ||
return j.literal(true); | ||
case 'ReservedPropTypeAnnotation': | ||
switch (typeAnnotation.name) { | ||
case 'ColorPrimitive': | ||
return j.template.expression`{ process: require('processColor') }`; | ||
return j.template.expression`{ process: require('react-native/Libraries/StyleSheet/processColor') }`; | ||
case 'ImageSourcePrimitive': | ||
return j.template | ||
.expression`{ process: require('resolveAssetSource') }`; | ||
return j.template.expression`{ process: require('react-native/Libraries/Image/resolveAssetSource') }`; | ||
case 'ImageRequestPrimitive': | ||
throw new Error('ImageRequest should not be used in props'); | ||
case 'PointPrimitive': | ||
return j.template.expression`{ diff: require('pointsDiffer') }`; | ||
return j.template.expression`{ diff: require('react-native/Libraries/Utilities/differ/pointsDiffer') }`; | ||
case 'EdgeInsetsPrimitive': | ||
return j.template.expression`{ diff: require('insetsDiffer') }`; | ||
return j.template.expression`{ diff: require('react-native/Libraries/Utilities/differ/insetsDiffer') }`; | ||
default: | ||
(typeAnnotation.name ); | ||
throw new Error( | ||
`Received unknown native typeAnnotation: "${typeAnnotation.name}"`, | ||
); | ||
typeAnnotation.name; | ||
throw new Error(`Received unknown native typeAnnotation: "${typeAnnotation.name}"`); | ||
} | ||
case 'ArrayTypeAnnotation': | ||
@@ -78,40 +82,61 @@ if (typeAnnotation.elementType.type === 'ReservedPropTypeAnnotation') { | ||
case 'ColorPrimitive': | ||
return j.template | ||
.expression`{ process: require('processColorArray') }`; | ||
return j.template.expression`{ process: require('react-native/Libraries/StyleSheet/processColorArray') }`; | ||
case 'ImageSourcePrimitive': | ||
return j.literal(true); | ||
case 'PointPrimitive': | ||
return j.literal(true); | ||
default: | ||
throw new Error( | ||
`Received unknown array native typeAnnotation: "${typeAnnotation.elementType.name}"`, | ||
); | ||
throw new Error(`Received unknown array native typeAnnotation: "${typeAnnotation.elementType.name}"`); | ||
} | ||
} | ||
return j.literal(true); | ||
default: | ||
(typeAnnotation ); | ||
throw new Error( | ||
`Received unknown typeAnnotation: "${typeAnnotation.type}"`, | ||
); | ||
typeAnnotation; | ||
throw new Error(`Received unknown typeAnnotation: "${typeAnnotation.type}"`); | ||
} | ||
} | ||
const componentTemplate = ` | ||
let nativeComponentName = '::_COMPONENT_NAME_WITH_COMPAT_SUPPORT_::'; | ||
::_DEPRECATION_CHECK_:: | ||
export default NativeComponentRegistry.get(nativeComponentName, () => VIEW_CONFIG); | ||
const ComponentTemplate = ({ | ||
componentName, | ||
paperComponentName, | ||
paperComponentNameDeprecated | ||
}) => { | ||
var _paperComponentName; | ||
const nativeComponentName = (_paperComponentName = paperComponentName) !== null && _paperComponentName !== void 0 ? _paperComponentName : componentName; | ||
return ` | ||
let nativeComponentName = '${nativeComponentName}'; | ||
${paperComponentNameDeprecated != null ? DeprecatedComponentNameCheckTemplate({ | ||
componentName, | ||
paperComponentNameDeprecated | ||
}) : ''} | ||
export const __INTERNAL_VIEW_CONFIG = VIEW_CONFIG; | ||
export default NativeComponentRegistry.get(nativeComponentName, () => __INTERNAL_VIEW_CONFIG); | ||
`.trim(); | ||
}; // Check whether the native component exists in the app binary. | ||
// Old getViewManagerConfig() checks for the existance of the native Paper view manager. Not available in Bridgeless. | ||
// New hasViewManagerConfig() queries Fabric’s native component registry directly. | ||
const deprecatedComponentTemplate = ` | ||
if (UIManager.getViewManagerConfig('::_COMPONENT_NAME_::')) { | ||
nativeComponentName = '::_COMPONENT_NAME_::'; | ||
} else if (UIManager.getViewManagerConfig('::_COMPONENT_NAME_DEPRECATED_::')) { | ||
nativeComponentName = '::_COMPONENT_NAME_DEPRECATED_::'; | ||
const DeprecatedComponentNameCheckTemplate = ({ | ||
componentName, | ||
paperComponentNameDeprecated | ||
}) => ` | ||
if (UIManager.hasViewManagerConfig('${componentName}')) { | ||
nativeComponentName = '${componentName}'; | ||
} else if (UIManager.hasViewManagerConfig('${paperComponentNameDeprecated}')) { | ||
nativeComponentName = '${paperComponentNameDeprecated}'; | ||
} else { | ||
throw new Error('Failed to find native component for either "::_COMPONENT_NAME_::" or "::_COMPONENT_NAME_DEPRECATED_::"'); | ||
throw new Error('Failed to find native component for either "${componentName}" or "${paperComponentNameDeprecated}"'); | ||
} | ||
`.trim(); | ||
`.trim(); // Replicates the behavior of RCTNormalizeInputEventName in RCTEventDispatcher.m | ||
// Replicates the behavior of RCTNormalizeInputEventName in RCTEventDispatcher.m | ||
function normalizeInputEventName(name) { | ||
@@ -125,55 +150,24 @@ if (name.startsWith('on')) { | ||
return name; | ||
} | ||
} // Replicates the behavior of viewConfig in RCTComponentData.m | ||
// Replicates the behavior of viewConfig in RCTComponentData.m | ||
function getValidAttributesForEvents(events) { | ||
return events.map(eventType => { | ||
function getValidAttributesForEvents(events, imports) { | ||
imports.add("const {ConditionallyIgnoredEventHandlers} = require('react-native/Libraries/NativeComponent/ViewConfigIgnore');"); | ||
const validAttributes = j.objectExpression(events.map(eventType => { | ||
return j.property('init', j.identifier(eventType.name), j.literal(true)); | ||
}); | ||
})); | ||
return j.callExpression(j.identifier('ConditionallyIgnoredEventHandlers'), [validAttributes]); | ||
} | ||
function generateBubblingEventInfo(event, nameOveride) { | ||
return j.property( | ||
'init', | ||
j.identifier(nameOveride || normalizeInputEventName(event.name)), | ||
j.objectExpression([ | ||
j.property( | ||
'init', | ||
j.identifier('phasedRegistrationNames'), | ||
j.objectExpression([ | ||
j.property( | ||
'init', | ||
j.identifier('captured'), | ||
j.literal(`${event.name}Capture`), | ||
), | ||
j.property('init', j.identifier('bubbled'), j.literal(event.name)), | ||
]), | ||
), | ||
]), | ||
); | ||
return j.property('init', j.identifier(nameOveride || normalizeInputEventName(event.name)), j.objectExpression([j.property('init', j.identifier('phasedRegistrationNames'), j.objectExpression([j.property('init', j.identifier('captured'), j.literal(`${event.name}Capture`)), j.property('init', j.identifier('bubbled'), j.literal(event.name))]))])); | ||
} | ||
function generateDirectEventInfo(event, nameOveride) { | ||
return j.property( | ||
'init', | ||
j.identifier(nameOveride || normalizeInputEventName(event.name)), | ||
j.objectExpression([ | ||
j.property( | ||
'init', | ||
j.identifier('registrationName'), | ||
j.literal(event.name), | ||
), | ||
]), | ||
); | ||
return j.property('init', j.identifier(nameOveride || normalizeInputEventName(event.name)), j.objectExpression([j.property('init', j.identifier('registrationName'), j.literal(event.name))])); | ||
} | ||
function buildViewConfig( | ||
schema , | ||
componentName , | ||
component, | ||
imports, | ||
) { | ||
function buildViewConfig(schema, componentName, component, imports) { | ||
const componentProps = component.props; | ||
const componentEvents = component.events; | ||
component.extendsProps.forEach(extendProps => { | ||
@@ -184,96 +178,49 @@ switch (extendProps.type) { | ||
case 'ReactNativeCoreViewProps': | ||
imports.add( | ||
"const NativeComponentRegistry = require('NativeComponentRegistry');", | ||
); | ||
imports.add("const NativeComponentRegistry = require('react-native/Libraries/NativeComponent/NativeComponentRegistry');"); | ||
return; | ||
return; | ||
default: | ||
(extendProps.knownTypeName ); | ||
extendProps.knownTypeName; | ||
throw new Error('Invalid knownTypeName'); | ||
} | ||
default: | ||
(extendProps.type ); | ||
extendProps.type; | ||
throw new Error('Invalid extended type'); | ||
} | ||
}); | ||
const validAttributes = j.objectExpression([ | ||
...componentProps.map(schemaProp => { | ||
return j.property( | ||
'init', | ||
j.identifier(schemaProp.name), | ||
getReactDiffProcessValue(schemaProp.typeAnnotation), | ||
); | ||
}), | ||
...getValidAttributesForEvents(componentEvents), | ||
]); | ||
const bubblingEventNames = component.events | ||
.filter(event => event.bubblingType === 'bubble') | ||
.reduce((bubblingEvents, event) => { | ||
// We add in the deprecated paper name so that it is in the view config. | ||
// This means either the old event name or the new event name can fire | ||
// and be sent to the listener until the old top level name is removed. | ||
if (event.paperTopLevelNameDeprecated) { | ||
bubblingEvents.push( | ||
generateBubblingEventInfo(event, event.paperTopLevelNameDeprecated), | ||
); | ||
} | ||
const validAttributes = j.objectExpression([...componentProps.map(schemaProp => { | ||
return j.property('init', j.identifier(schemaProp.name), getReactDiffProcessValue(schemaProp.typeAnnotation)); | ||
}), ...(componentEvents.length > 0 ? [j.spreadProperty(getValidAttributesForEvents(componentEvents, imports))] : [])]); | ||
const bubblingEventNames = component.events.filter(event => event.bubblingType === 'bubble').reduce((bubblingEvents, event) => { | ||
// We add in the deprecated paper name so that it is in the view config. | ||
// This means either the old event name or the new event name can fire | ||
// and be sent to the listener until the old top level name is removed. | ||
if (event.paperTopLevelNameDeprecated) { | ||
bubblingEvents.push(generateBubblingEventInfo(event, event.paperTopLevelNameDeprecated)); | ||
} else { | ||
bubblingEvents.push(generateBubblingEventInfo(event)); | ||
return bubblingEvents; | ||
}, []); | ||
} | ||
const bubblingEvents = | ||
bubblingEventNames.length > 0 | ||
? j.property( | ||
'init', | ||
j.identifier('bubblingEventTypes'), | ||
j.objectExpression(bubblingEventNames), | ||
) | ||
: null; | ||
const directEventNames = component.events | ||
.filter(event => event.bubblingType === 'direct') | ||
.reduce((directEvents, event) => { | ||
// We add in the deprecated paper name so that it is in the view config. | ||
// This means either the old event name or the new event name can fire | ||
// and be sent to the listener until the old top level name is removed. | ||
if (event.paperTopLevelNameDeprecated) { | ||
directEvents.push( | ||
generateDirectEventInfo(event, event.paperTopLevelNameDeprecated), | ||
); | ||
} | ||
return bubblingEvents; | ||
}, []); | ||
const bubblingEvents = bubblingEventNames.length > 0 ? j.property('init', j.identifier('bubblingEventTypes'), j.objectExpression(bubblingEventNames)) : null; | ||
const directEventNames = component.events.filter(event => event.bubblingType === 'direct').reduce((directEvents, event) => { | ||
// We add in the deprecated paper name so that it is in the view config. | ||
// This means either the old event name or the new event name can fire | ||
// and be sent to the listener until the old top level name is removed. | ||
if (event.paperTopLevelNameDeprecated) { | ||
directEvents.push(generateDirectEventInfo(event, event.paperTopLevelNameDeprecated)); | ||
} else { | ||
directEvents.push(generateDirectEventInfo(event)); | ||
return directEvents; | ||
}, []); | ||
} | ||
const directEvents = | ||
directEventNames.length > 0 | ||
? j.property( | ||
'init', | ||
j.identifier('directEventTypes'), | ||
j.objectExpression(directEventNames), | ||
) | ||
: null; | ||
const properties = [ | ||
j.property( | ||
'init', | ||
j.identifier('uiViewClassName'), | ||
j.literal(componentName), | ||
), | ||
bubblingEvents, | ||
directEvents, | ||
j.property('init', j.identifier('validAttributes'), validAttributes), | ||
].filter(Boolean); | ||
return directEvents; | ||
}, []); | ||
const directEvents = directEventNames.length > 0 ? j.property('init', j.identifier('directEventTypes'), j.objectExpression(directEventNames)) : null; | ||
const properties = [j.property('init', j.identifier('uiViewClassName'), j.literal(componentName)), bubblingEvents, directEvents, j.property('init', j.identifier('validAttributes'), validAttributes)].filter(Boolean); | ||
return j.objectExpression(properties); | ||
} | ||
function buildCommands( | ||
schema , | ||
componentName , | ||
component, | ||
imports, | ||
) { | ||
function buildCommands(schema, componentName, component, imports) { | ||
const commands = component.commands; | ||
@@ -285,141 +232,71 @@ | ||
imports.add( | ||
'const {dispatchCommand} = require("react-native/Libraries/Renderer/shims/ReactNative");', | ||
); | ||
imports.add('const {dispatchCommand} = require("react-native/Libraries/ReactNative/RendererProxy");'); | ||
const properties = commands.map(command => { | ||
const commandName = command.name; | ||
const params = command.typeAnnotation.params; | ||
const commandNameLiteral = j.literal(commandName); | ||
const commandNameIdentifier = j.identifier(commandName); | ||
const arrayParams = j.arrayExpression( | ||
params.map(param => { | ||
return j.identifier(param.name); | ||
}), | ||
); | ||
const expression = j.template | ||
.expression`dispatchCommand(ref, ${commandNameLiteral}, ${arrayParams})`; | ||
const arrayParams = j.arrayExpression(params.map(param => { | ||
return j.identifier(param.name); | ||
})); | ||
const expression = j.template.expression`dispatchCommand(ref, ${commandNameLiteral}, ${arrayParams})`; | ||
const functionParams = params.map(param => { | ||
return j.identifier(param.name); | ||
}); | ||
const property = j.property( | ||
'init', | ||
commandNameIdentifier, | ||
j.functionExpression( | ||
null, | ||
[j.identifier('ref'), ...functionParams], | ||
j.blockStatement([j.expressionStatement(expression)]), | ||
), | ||
); | ||
const property = j.property('init', commandNameIdentifier, j.functionExpression(null, [j.identifier('ref'), ...functionParams], j.blockStatement([j.expressionStatement(expression)]))); | ||
property.method = true; | ||
return property; | ||
}); | ||
return j.exportNamedDeclaration( | ||
j.variableDeclaration('const', [ | ||
j.variableDeclarator( | ||
j.identifier('Commands'), | ||
j.objectExpression(properties), | ||
), | ||
]), | ||
); | ||
return j.exportNamedDeclaration(j.variableDeclaration('const', [j.variableDeclarator(j.identifier('Commands'), j.objectExpression(properties))])); | ||
} | ||
module.exports = { | ||
generate(libraryName , schema ) { | ||
generate(libraryName, schema) { | ||
try { | ||
const fileName = `${libraryName}NativeViewConfig.js`; | ||
const imports = new Set(); | ||
const imports = new Set(); | ||
const moduleResults = Object.keys(schema.modules).map(moduleName => { | ||
const module = schema.modules[moduleName]; | ||
const moduleResults = Object.keys(schema.modules) | ||
.map(moduleName => { | ||
const module = schema.modules[moduleName]; | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
const {components} = module; | ||
const components = module.components; | ||
return Object.keys(components).map(componentName => { | ||
var _component$paperCompo; | ||
return Object.keys(components) | ||
.map((componentName ) => { | ||
const component = components[componentName]; | ||
const component = components[componentName]; | ||
const paperComponentName = component.paperComponentName | ||
? component.paperComponentName | ||
: componentName; | ||
if (component.paperComponentNameDeprecated) { | ||
imports.add(UIMANAGER_IMPORT); | ||
} | ||
if (component.paperComponentNameDeprecated) { | ||
imports.add(UIMANAGER_IMPORT); | ||
} | ||
const replacedTemplate = ComponentTemplate({ | ||
componentName, | ||
paperComponentName: component.paperComponentName, | ||
paperComponentNameDeprecated: component.paperComponentNameDeprecated | ||
}); | ||
const replacedSourceRoot = j.withParser('flow')(replacedTemplate); | ||
const paperComponentName = (_component$paperCompo = component.paperComponentName) !== null && _component$paperCompo !== void 0 ? _component$paperCompo : componentName; | ||
replacedSourceRoot.find(j.Identifier, { | ||
name: 'VIEW_CONFIG' | ||
}).replaceWith(buildViewConfig(schema, paperComponentName, component, imports)); | ||
const commands = buildCommands(schema, paperComponentName, component, imports); | ||
const deprecatedCheckBlock = component.paperComponentNameDeprecated | ||
? deprecatedComponentTemplate | ||
.replace(/::_COMPONENT_NAME_::/g, componentName) | ||
.replace( | ||
/::_COMPONENT_NAME_DEPRECATED_::/g, | ||
component.paperComponentNameDeprecated || '', | ||
) | ||
: ''; | ||
if (commands) { | ||
replacedSourceRoot.find(j.ExportDefaultDeclaration).insertAfter(j(commands).toSource()); | ||
} | ||
const replacedTemplate = componentTemplate | ||
.replace(/::_COMPONENT_NAME_::/g, componentName) | ||
.replace( | ||
/::_COMPONENT_NAME_WITH_COMPAT_SUPPORT_::/g, | ||
paperComponentName, | ||
) | ||
.replace(/::_DEPRECATION_CHECK_::/, deprecatedCheckBlock); | ||
const replacedSourceRoot = j.withParser('flow')(replacedTemplate); | ||
replacedSourceRoot | ||
.find(j.Identifier, { | ||
name: 'VIEW_CONFIG', | ||
}) | ||
.replaceWith( | ||
buildViewConfig( | ||
schema, | ||
paperComponentName, | ||
component, | ||
imports, | ||
), | ||
); | ||
const commands = buildCommands( | ||
schema, | ||
paperComponentName, | ||
component, | ||
imports, | ||
); | ||
if (commands) { | ||
replacedSourceRoot | ||
.find(j.ExportDefaultDeclaration) | ||
.insertAfter(j(commands).toSource()); | ||
} | ||
const replacedSource = replacedSourceRoot.toSource({ | ||
quote: 'single', | ||
trailingComma: true, | ||
}); | ||
return replacedSource; | ||
}) | ||
.join('\n\n'); | ||
}) | ||
.filter(Boolean) | ||
.join('\n\n'); | ||
const replacedTemplate = template | ||
.replace(/::_COMPONENT_CONFIG_::/g, moduleResults) | ||
.replace( | ||
'::_IMPORTS_::', | ||
Array.from(imports) | ||
.sort() | ||
.join('\n'), | ||
); | ||
const replacedSource = replacedSourceRoot.toSource({ | ||
quote: 'single', | ||
trailingComma: true | ||
}); | ||
return replacedSource; | ||
}).join('\n\n'); | ||
}).filter(Boolean).join('\n\n'); | ||
const replacedTemplate = FileTemplate({ | ||
componentConfig: moduleResults, | ||
imports: Array.from(imports).sort().join('\n') | ||
}); | ||
return new Map([[fileName, replacedTemplate]]); | ||
@@ -431,3 +308,4 @@ } catch (error) { | ||
} | ||
}, | ||
}; | ||
} | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,23 +10,17 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
function upperCaseFirst(inString ) { | ||
function upperCaseFirst(inString) { | ||
return inString[0].toUpperCase() + inString.slice(1); | ||
} | ||
function getInterfaceJavaClassName(componentName ) { | ||
function getInterfaceJavaClassName(componentName) { | ||
return `${componentName.replace(/^RCT/, '')}ManagerInterface`; | ||
} | ||
function getDelegateJavaClassName(componentName ) { | ||
function getDelegateJavaClassName(componentName) { | ||
return `${componentName.replace(/^RCT/, '')}ManagerDelegate`; | ||
} | ||
function toSafeJavaString( | ||
input , | ||
shouldUpperCaseFirst , | ||
) { | ||
function toSafeJavaString(input, shouldUpperCaseFirst) { | ||
const parts = input.split('-'); | ||
@@ -41,8 +35,4 @@ | ||
function getImports( | ||
component , | ||
type , | ||
) { | ||
const imports = new Set(); | ||
function getImports(component, type) { | ||
const imports = new Set(); | ||
component.extendsProps.forEach(extendProps => { | ||
@@ -55,8 +45,10 @@ switch (extendProps.type) { | ||
return; | ||
default: | ||
(extendProps.knownTypeName ); | ||
extendProps.knownTypeName; | ||
throw new Error('Invalid knownTypeName'); | ||
} | ||
default: | ||
(extendProps.type ); | ||
extendProps.type; | ||
throw new Error('Invalid extended type'); | ||
@@ -72,14 +64,19 @@ } | ||
} | ||
return; | ||
case 'ImageSourcePrimitive': | ||
imports.add('import com.facebook.react.bridge.ReadableMap;'); | ||
return; | ||
case 'PointPrimitive': | ||
imports.add('import com.facebook.react.bridge.ReadableMap;'); | ||
return; | ||
case 'EdgeInsetsPrimitive': | ||
imports.add('import com.facebook.react.bridge.ReadableMap;'); | ||
return; | ||
default: | ||
(name ); | ||
name; | ||
throw new Error(`Invalid ReservedPropTypeAnnotation name, got ${name}`); | ||
@@ -104,3 +101,2 @@ } | ||
}); | ||
return imports; | ||
@@ -113,3 +109,3 @@ } | ||
toSafeJavaString, | ||
getImports, | ||
}; | ||
getImports | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,38 +10,31 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); } | ||
const {createAliasResolver, getModules} = require('./Utils'); | ||
const {unwrapNullable} = require('../../parsers/flow/modules/utils'); | ||
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } | ||
function _iterableToArrayLimit(arr, i) { if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } | ||
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } | ||
const _require = require('./Utils'), | ||
createAliasResolver = _require.createAliasResolver, | ||
getModules = _require.getModules; | ||
const _require2 = require('../../parsers/parsers-commons'), | ||
unwrapNullable = _require2.unwrapNullable; | ||
const HostFunctionTemplate = ({ | ||
hasteModuleName, | ||
methodName, | ||
isVoid, | ||
args, | ||
} | ||
) => { | ||
returnTypeAnnotation, | ||
args | ||
}) => { | ||
const isNullable = returnTypeAnnotation.type === 'NullableTypeAnnotation'; | ||
const isVoid = returnTypeAnnotation.type === 'VoidTypeAnnotation'; | ||
const methodCallArgs = ['rt', ...args].join(', '); | ||
const methodCall = `static_cast<${hasteModuleName}CxxSpecJSI *>(&turboModule)->${methodName}(${methodCallArgs});`; | ||
return `static jsi::Value __hostFunction_${hasteModuleName}CxxSpecJSI_${methodName}(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {${ | ||
isVoid ? `\n ${methodCall}` : '' | ||
} | ||
return ${isVoid ? 'jsi::Value::undefined();' : methodCall} | ||
const methodCall = `static_cast<${hasteModuleName}CxxSpecJSI *>(&turboModule)->${methodName}(${methodCallArgs})`; | ||
return `static jsi::Value __hostFunction_${hasteModuleName}CxxSpecJSI_${methodName}(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {${isVoid ? `\n ${methodCall};` : isNullable ? `\n auto result = ${methodCall};` : ''} | ||
return ${isVoid ? 'jsi::Value::undefined()' : isNullable ? 'result ? jsi::Value(std::move(*result)) : jsi::Value::null()' : methodCall}; | ||
}`; | ||
@@ -54,9 +47,4 @@ }; | ||
moduleName, | ||
methods, | ||
} | ||
) => { | ||
methods | ||
}) => { | ||
return `${hostFunctions.join('\n')} | ||
@@ -66,7 +54,8 @@ | ||
: TurboModule("${moduleName}", jsInvoker) { | ||
${methods | ||
.map(({methodName, paramCount}) => { | ||
${methods.map(({ | ||
methodName, | ||
paramCount | ||
}) => { | ||
return ` methodMap_["${methodName}"] = MethodMetadata {${paramCount}, __hostFunction_${hasteModuleName}CxxSpecJSI_${methodName}};`; | ||
}) | ||
.join('\n')} | ||
}).join('\n')} | ||
}`; | ||
@@ -77,12 +66,9 @@ }; | ||
libraryName, | ||
modules, | ||
} | ||
) => { | ||
modules | ||
}) => { | ||
return `/** | ||
* ${'C'}opyright (c) Facebook, Inc. and its affiliates. | ||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* Do not edit this file as changes may cause incorrect behavior and will be lost | ||
* once the code is regenerated. | ||
* | ||
@@ -92,3 +78,3 @@ * ${'@'}generated by codegen project: GenerateModuleH.js | ||
#include <react/modules/${libraryName}/NativeModules.h> | ||
#include "${libraryName}JSI.h" | ||
@@ -106,18 +92,14 @@ namespace facebook { | ||
function serializeArg(arg, index, resolveAlias) { | ||
const nullableTypeAnnotation = arg.typeAnnotation, | ||
optional = arg.optional; | ||
function serializeArg( | ||
arg , | ||
index , | ||
resolveAlias , | ||
) { | ||
function wrap(suffix) { | ||
return `args[${index}]${suffix}`; | ||
} | ||
const {typeAnnotation: nullableTypeAnnotation} = arg; | ||
const [typeAnnotation] = unwrapNullable ( | ||
nullableTypeAnnotation, | ||
); | ||
const _unwrapNullable = unwrapNullable(nullableTypeAnnotation), | ||
_unwrapNullable2 = _slicedToArray(_unwrapNullable, 2), | ||
typeAnnotation = _unwrapNullable2[0], | ||
nullable = _unwrapNullable2[1]; | ||
const isRequired = !optional && !nullable; | ||
let realTypeAnnotation = typeAnnotation; | ||
if (realTypeAnnotation.type === 'TypeAliasTypeAnnotation') { | ||
@@ -127,2 +109,19 @@ realTypeAnnotation = resolveAlias(realTypeAnnotation.name); | ||
function wrap(callback) { | ||
const val = `args[${index}]`; | ||
const expression = callback(val); | ||
if (isRequired) { | ||
return expression; | ||
} else { | ||
let condition = `${val}.isNull() || ${val}.isUndefined()`; | ||
if (optional) { | ||
condition = `count < ${index} || ${condition}`; | ||
} | ||
return `${condition} ? std::nullopt : std::make_optional(${expression})`; | ||
} | ||
} | ||
switch (realTypeAnnotation.type) { | ||
@@ -132,49 +131,79 @@ case 'ReservedTypeAnnotation': | ||
case 'RootTag': | ||
return wrap('.getNumber()'); | ||
return wrap(val => `${val}.getNumber()`); | ||
default: | ||
(realTypeAnnotation.name ); | ||
throw new Error( | ||
`Unknown prop type for "${arg.name}, found: ${realTypeAnnotation.name}"`, | ||
); | ||
realTypeAnnotation.name; | ||
throw new Error(`Unknown prop type for "${arg.name}, found: ${realTypeAnnotation.name}"`); | ||
} | ||
case 'StringTypeAnnotation': | ||
return wrap('.getString(rt)'); | ||
return wrap(val => `${val}.asString(rt)`); | ||
case 'BooleanTypeAnnotation': | ||
return wrap('.getBool()'); | ||
return wrap(val => `${val}.asBool()`); | ||
case 'EnumDeclaration': | ||
switch (realTypeAnnotation.memberType) { | ||
case 'NumberTypeAnnotation': | ||
return wrap(val => `${val}.asNumber()`); | ||
case 'StringTypeAnnotation': | ||
return wrap(val => `${val}.asString(rt)`); | ||
default: | ||
throw new Error(`Unknown enum type for "${arg.name}, found: ${realTypeAnnotation.type}"`); | ||
} | ||
case 'NumberTypeAnnotation': | ||
return wrap('.getNumber()'); | ||
return wrap(val => `${val}.asNumber()`); | ||
case 'FloatTypeAnnotation': | ||
return wrap('.getNumber()'); | ||
return wrap(val => `${val}.asNumber()`); | ||
case 'DoubleTypeAnnotation': | ||
return wrap('.getNumber()'); | ||
return wrap(val => `${val}.asNumber()`); | ||
case 'Int32TypeAnnotation': | ||
return wrap('.getNumber()'); | ||
return wrap(val => `${val}.asNumber()`); | ||
case 'ArrayTypeAnnotation': | ||
return wrap('.getObject(rt).getArray(rt)'); | ||
return wrap(val => `${val}.asObject(rt).asArray(rt)`); | ||
case 'FunctionTypeAnnotation': | ||
return `std::move(${wrap('.getObject(rt).getFunction(rt)')})`; | ||
return wrap(val => `${val}.asObject(rt).asFunction(rt)`); | ||
case 'GenericObjectTypeAnnotation': | ||
return wrap('.getObject(rt)'); | ||
return wrap(val => `${val}.asObject(rt)`); | ||
case 'UnionTypeAnnotation': | ||
switch (typeAnnotation.memberType) { | ||
case 'NumberTypeAnnotation': | ||
return wrap(val => `${val}.asNumber()`); | ||
case 'ObjectTypeAnnotation': | ||
return wrap(val => `${val}.asObject(rt)`); | ||
case 'StringTypeAnnotation': | ||
return wrap(val => `${val}.asString(rt)`); | ||
default: | ||
throw new Error(`Unsupported union member type for param "${arg.name}, found: ${realTypeAnnotation.memberType}"`); | ||
} | ||
case 'ObjectTypeAnnotation': | ||
return wrap('.getObject(rt)'); | ||
return wrap(val => `${val}.asObject(rt)`); | ||
case 'MixedTypeAnnotation': | ||
return wrap(val => `jsi::Value(rt, ${val})`); | ||
default: | ||
(realTypeAnnotation.type ); | ||
throw new Error( | ||
`Unknown prop type for "${arg.name}, found: ${realTypeAnnotation.type}"`, | ||
); | ||
realTypeAnnotation.type; | ||
throw new Error(`Unknown prop type for "${arg.name}, found: ${realTypeAnnotation.type}"`); | ||
} | ||
} | ||
function serializePropertyIntoHostFunction( | ||
hasteModuleName , | ||
property , | ||
resolveAlias , | ||
) { | ||
const [ | ||
propertyTypeAnnotation, | ||
] = unwrapNullable ( | ||
property.typeAnnotation, | ||
); | ||
const isVoid = | ||
propertyTypeAnnotation.returnTypeAnnotation.type === 'VoidTypeAnnotation'; | ||
function serializePropertyIntoHostFunction(hasteModuleName, property, resolveAlias) { | ||
const _unwrapNullable3 = unwrapNullable(property.typeAnnotation), | ||
_unwrapNullable4 = _slicedToArray(_unwrapNullable3, 1), | ||
propertyTypeAnnotation = _unwrapNullable4[0]; | ||
@@ -184,6 +213,4 @@ return HostFunctionTemplate({ | ||
methodName: property.name, | ||
isVoid, | ||
args: propertyTypeAnnotation.params.map((p, i) => | ||
serializeArg(p, i, resolveAlias), | ||
), | ||
returnTypeAnnotation: propertyTypeAnnotation.returnTypeAnnotation, | ||
args: propertyTypeAnnotation.params.map((p, i) => serializeArg(p, i, resolveAlias)) | ||
}); | ||
@@ -193,52 +220,39 @@ } | ||
module.exports = { | ||
generate( | ||
libraryName , | ||
schema , | ||
packageName , | ||
assumeNonnull = false, | ||
) { | ||
generate(libraryName, schema, packageName, assumeNonnull = false) { | ||
const nativeModules = getModules(schema); | ||
const modules = Object.keys(nativeModules).map(hasteModuleName => { | ||
const nativeModule = nativeModules[hasteModuleName]; | ||
const aliases = nativeModule.aliases, | ||
properties = nativeModule.spec.properties, | ||
moduleNames = nativeModule.moduleNames; | ||
const resolveAlias = createAliasResolver(aliases); | ||
const hostFunctions = properties.map(property => serializePropertyIntoHostFunction(hasteModuleName, property, resolveAlias)); | ||
return ModuleTemplate({ | ||
hasteModuleName, | ||
hostFunctions, | ||
// TODO: What happens when there are more than one NativeModule requires? | ||
moduleName: moduleNames[0], | ||
methods: properties.map(({ | ||
name: propertyName, | ||
typeAnnotation: nullableTypeAnnotation | ||
}) => { | ||
const _unwrapNullable5 = unwrapNullable(nullableTypeAnnotation), | ||
_unwrapNullable6 = _slicedToArray(_unwrapNullable5, 1), | ||
params = _unwrapNullable6[0].params; | ||
const modules = Object.keys(nativeModules) | ||
.map((hasteModuleName ) => { | ||
const nativeModule = nativeModules[hasteModuleName]; | ||
const { | ||
aliases, | ||
spec: {properties}, | ||
moduleNames, | ||
} = nativeModule; | ||
const resolveAlias = createAliasResolver(aliases); | ||
const hostFunctions = properties.map(property => | ||
serializePropertyIntoHostFunction( | ||
hasteModuleName, | ||
property, | ||
resolveAlias, | ||
), | ||
); | ||
return ModuleTemplate({ | ||
hasteModuleName, | ||
hostFunctions, | ||
// TODO: What happens when there are more than one NativeModule requires? | ||
moduleName: moduleNames[0], | ||
methods: properties.map( | ||
({name: propertyName, typeAnnotation: nullableTypeAnnotation}) => { | ||
const [{params}] = unwrapNullable(nullableTypeAnnotation); | ||
return { | ||
methodName: propertyName, | ||
paramCount: params.length, | ||
}; | ||
}, | ||
), | ||
}); | ||
}) | ||
.join('\n'); | ||
const fileName = 'NativeModules.cpp'; | ||
return { | ||
methodName: propertyName, | ||
paramCount: params.length | ||
}; | ||
}) | ||
}); | ||
}).join('\n'); | ||
const fileName = `${libraryName}JSI-generated.cpp`; | ||
const replacedTemplate = FileTemplate({ | ||
modules, | ||
libraryName, | ||
libraryName | ||
}); | ||
return new Map([[fileName, replacedTemplate]]); | ||
}, | ||
}; | ||
} | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,23 +10,28 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); } | ||
const {createAliasResolver, getModules} = require('./Utils'); | ||
const {unwrapNullable} = require('../../parsers/flow/modules/utils'); | ||
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } | ||
function _iterableToArrayLimit(arr, i) { if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } | ||
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } | ||
const _require = require('./Utils'), | ||
createAliasResolver = _require.createAliasResolver, | ||
getModules = _require.getModules; | ||
const _require2 = require('../Utils'), | ||
indent = _require2.indent; | ||
const _require3 = require('../../parsers/parsers-commons'), | ||
unwrapNullable = _require3.unwrapNullable; | ||
const ModuleClassDeclarationTemplate = ({ | ||
hasteModuleName, | ||
moduleProperties, | ||
} ) => { | ||
return `class JSI_EXPORT ${hasteModuleName}CxxSpecJSI : public TurboModule { | ||
structs | ||
}) => { | ||
return `${structs}class JSI_EXPORT ${hasteModuleName}CxxSpecJSI : public TurboModule { | ||
protected: | ||
@@ -36,3 +41,3 @@ ${hasteModuleName}CxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker); | ||
public: | ||
${moduleProperties} | ||
${indent(moduleProperties.join('\n'), 2)} | ||
@@ -42,12 +47,43 @@ };`; | ||
const ModuleSpecClassDeclarationTemplate = ({ | ||
hasteModuleName, | ||
moduleName, | ||
moduleProperties | ||
}) => { | ||
return `template <typename T> | ||
class JSI_EXPORT ${hasteModuleName}CxxSpec : public TurboModule { | ||
public: | ||
jsi::Value get(jsi::Runtime &rt, const jsi::PropNameID &propName) override { | ||
return delegate_.get(rt, propName); | ||
} | ||
protected: | ||
${hasteModuleName}CxxSpec(std::shared_ptr<CallInvoker> jsInvoker) | ||
: TurboModule("${moduleName}", jsInvoker), | ||
delegate_(static_cast<T*>(this), jsInvoker) {} | ||
private: | ||
class Delegate : public ${hasteModuleName}CxxSpecJSI { | ||
public: | ||
Delegate(T *instance, std::shared_ptr<CallInvoker> jsInvoker) : | ||
${hasteModuleName}CxxSpecJSI(std::move(jsInvoker)), instance_(instance) {} | ||
${indent(moduleProperties.join('\n'), 4)} | ||
private: | ||
T *instance_; | ||
}; | ||
Delegate delegate_; | ||
};`; | ||
}; | ||
const FileTemplate = ({ | ||
modules, | ||
} | ||
) => { | ||
modules | ||
}) => { | ||
return `/** | ||
* ${'C'}opyright (c) Facebook, Inc. and its affiliates. | ||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* Do not edit this file as changes may cause incorrect behavior and will be lost | ||
* once the code is regenerated. | ||
* | ||
@@ -60,7 +96,9 @@ * ${'@'}generated by codegen project: GenerateModuleH.js | ||
#include <ReactCommon/TurboModule.h> | ||
#include <react/bridging/Bridging.h> | ||
namespace facebook { | ||
namespace react { | ||
${modules} | ||
${modules.join('\n\n')} | ||
} // namespace react | ||
@@ -71,11 +109,11 @@ } // namespace facebook | ||
function translatePrimitiveJSTypeToCpp( | ||
nullableTypeAnnotation , | ||
createErrorMessage , | ||
resolveAlias , | ||
) { | ||
const [typeAnnotation] = unwrapNullable ( | ||
nullableTypeAnnotation, | ||
); | ||
function translatePrimitiveJSTypeToCpp(nullableTypeAnnotation, optional, createErrorMessage, resolveAlias) { | ||
const _unwrapNullable = unwrapNullable(nullableTypeAnnotation), | ||
_unwrapNullable2 = _slicedToArray(_unwrapNullable, 2), | ||
typeAnnotation = _unwrapNullable2[0], | ||
nullable = _unwrapNullable2[1]; | ||
const isRequired = !optional && !nullable; | ||
let realTypeAnnotation = typeAnnotation; | ||
if (realTypeAnnotation.type === 'TypeAliasTypeAnnotation') { | ||
@@ -85,2 +123,6 @@ realTypeAnnotation = resolveAlias(realTypeAnnotation.name); | ||
function wrap(type) { | ||
return isRequired ? type : `std::optional<${type}>`; | ||
} | ||
switch (realTypeAnnotation.type) { | ||
@@ -90,33 +132,77 @@ case 'ReservedTypeAnnotation': | ||
case 'RootTag': | ||
return 'double'; | ||
return wrap('double'); | ||
default: | ||
(realTypeAnnotation.name ); | ||
realTypeAnnotation.name; | ||
throw new Error(createErrorMessage(realTypeAnnotation.name)); | ||
} | ||
case 'VoidTypeAnnotation': | ||
return 'void'; | ||
case 'StringTypeAnnotation': | ||
return 'jsi::String'; | ||
return wrap('jsi::String'); | ||
case 'NumberTypeAnnotation': | ||
return 'double'; | ||
return wrap('double'); | ||
case 'DoubleTypeAnnotation': | ||
return 'double'; | ||
return wrap('double'); | ||
case 'FloatTypeAnnotation': | ||
return 'double'; | ||
return wrap('double'); | ||
case 'Int32TypeAnnotation': | ||
return 'int'; | ||
return wrap('int'); | ||
case 'BooleanTypeAnnotation': | ||
return 'bool'; | ||
return wrap('bool'); | ||
case 'EnumDeclaration': | ||
switch (realTypeAnnotation.memberType) { | ||
case 'NumberTypeAnnotation': | ||
return wrap('double'); | ||
case 'StringTypeAnnotation': | ||
return wrap('jsi::String'); | ||
default: | ||
throw new Error(createErrorMessage(realTypeAnnotation.type)); | ||
} | ||
case 'GenericObjectTypeAnnotation': | ||
return 'jsi::Object'; | ||
return wrap('jsi::Object'); | ||
case 'UnionTypeAnnotation': | ||
switch (typeAnnotation.memberType) { | ||
case 'NumberTypeAnnotation': | ||
return wrap('double'); | ||
case 'ObjectTypeAnnotation': | ||
return wrap('jsi::Object'); | ||
case 'StringTypeAnnotation': | ||
return wrap('jsi::String'); | ||
default: | ||
throw new Error(createErrorMessage(realTypeAnnotation.type)); | ||
} | ||
case 'ObjectTypeAnnotation': | ||
return 'jsi::Object'; | ||
return wrap('jsi::Object'); | ||
case 'ArrayTypeAnnotation': | ||
return 'jsi::Array'; | ||
return wrap('jsi::Array'); | ||
case 'FunctionTypeAnnotation': | ||
return 'jsi::Function'; | ||
return wrap('jsi::Function'); | ||
case 'PromiseTypeAnnotation': | ||
return 'jsi::Value'; | ||
return wrap('jsi::Value'); | ||
case 'MixedTypeAnnotation': | ||
return wrap('jsi::Value'); | ||
default: | ||
(realTypeAnnotation.type ); | ||
realTypeAnnotation.type; | ||
throw new Error(createErrorMessage(realTypeAnnotation.type)); | ||
@@ -126,75 +212,113 @@ } | ||
const propertyTemplate = | ||
'virtual ::_RETURN_VALUE_:: ::_PROPERTY_NAME_::(jsi::Runtime &rt::_ARGS_::) = 0;'; | ||
function createStructs(moduleName, aliasMap, resolveAlias) { | ||
return Object.keys(aliasMap).map(alias => { | ||
const value = aliasMap[alias]; | ||
module.exports = { | ||
generate( | ||
libraryName , | ||
schema , | ||
packageName , | ||
assumeNonnull = false, | ||
) { | ||
const nativeModules = getModules(schema); | ||
if (value.properties.length === 0) { | ||
return ''; | ||
} | ||
const modules = Object.keys(nativeModules) | ||
.map(hasteModuleName => { | ||
const { | ||
aliases, | ||
spec: {properties}, | ||
} = nativeModules[hasteModuleName]; | ||
const resolveAlias = createAliasResolver(aliases); | ||
const structName = `${moduleName}Base${alias}`; | ||
const templateParameterWithTypename = value.properties.map((v, i) => 'typename P' + i).join(', '); | ||
const templateParameter = value.properties.map((v, i) => 'P' + i).join(', '); | ||
return `#pragma mark - ${structName} | ||
const traversedProperties = properties | ||
.map(prop => { | ||
const [ | ||
propTypeAnnotation, | ||
] = unwrapNullable ( | ||
prop.typeAnnotation, | ||
); | ||
const traversedArgs = propTypeAnnotation.params | ||
.map(param => { | ||
const translatedParam = translatePrimitiveJSTypeToCpp( | ||
param.typeAnnotation, | ||
typeName => | ||
`Unsupported type for param "${param.name}" in ${prop.name}. Found: ${typeName}`, | ||
resolveAlias, | ||
); | ||
const isObject = translatedParam.startsWith('jsi::'); | ||
return ( | ||
(isObject | ||
? 'const ' + translatedParam + ' &' | ||
: translatedParam + ' ') + param.name | ||
); | ||
}) | ||
.join(', '); | ||
return propertyTemplate | ||
.replace('::_PROPERTY_NAME_::', prop.name) | ||
.replace( | ||
'::_RETURN_VALUE_::', | ||
translatePrimitiveJSTypeToCpp( | ||
propTypeAnnotation.returnTypeAnnotation, | ||
typeName => | ||
`Unsupported return type for ${prop.name}. Found: ${typeName}`, | ||
resolveAlias, | ||
), | ||
) | ||
.replace( | ||
'::_ARGS_::', | ||
traversedArgs === '' ? '' : ', ' + traversedArgs, | ||
); | ||
}) | ||
.join('\n'); | ||
template <${templateParameterWithTypename}> | ||
struct ${structName} { | ||
${value.properties.map((v, i) => ' P' + i + ' ' + v.name).join(';\n')}; | ||
bool operator==(const ${structName} &other) const { | ||
return ${value.properties.map(v => `${v.name} == other.${v.name}`).join(' && ')}; | ||
} | ||
}; | ||
return ModuleClassDeclarationTemplate({ | ||
hasteModuleName, | ||
moduleProperties: traversedProperties, | ||
}); | ||
}) | ||
.join('\n'); | ||
template <${templateParameterWithTypename}> | ||
struct ${structName}Bridging { | ||
static ${structName}<${templateParameter}> fromJs( | ||
jsi::Runtime &rt, | ||
const jsi::Object &value, | ||
const std::shared_ptr<CallInvoker> &jsInvoker) { | ||
${structName}<${templateParameter}> result{ | ||
${value.properties.map((v, i) => ` bridging::fromJs<P${i}>(rt, value.getProperty(rt, "${v.name}"), jsInvoker)`).join(',\n')}}; | ||
return result; | ||
} | ||
const fileName = 'NativeModules.h'; | ||
const replacedTemplate = FileTemplate({modules}); | ||
static jsi::Object toJs( | ||
jsi::Runtime &rt, | ||
const ${structName}<${templateParameter}> &value) { | ||
auto result = facebook::jsi::Object(rt); | ||
${value.properties.map((v, i) => { | ||
if (v.optional) { | ||
return ` if (value.${v.name}) { | ||
result.setProperty(rt, "${v.name}", bridging::toJs(rt, value.${v.name}.value())); | ||
}`; | ||
} else { | ||
return ` result.setProperty(rt, "${v.name}", bridging::toJs(rt, value.${v.name}));`; | ||
} | ||
}).join('\n')} | ||
return result; | ||
} | ||
}; | ||
`; | ||
}).join('\n'); | ||
} | ||
function translatePropertyToCpp(prop, resolveAlias, abstract = false) { | ||
const _unwrapNullable3 = unwrapNullable(prop.typeAnnotation), | ||
_unwrapNullable4 = _slicedToArray(_unwrapNullable3, 1), | ||
propTypeAnnotation = _unwrapNullable4[0]; | ||
const params = propTypeAnnotation.params.map(param => `std::move(${param.name})`); | ||
const paramTypes = propTypeAnnotation.params.map(param => { | ||
const translatedParam = translatePrimitiveJSTypeToCpp(param.typeAnnotation, param.optional, typeName => `Unsupported type for param "${param.name}" in ${prop.name}. Found: ${typeName}`, resolveAlias); | ||
return `${translatedParam} ${param.name}`; | ||
}); | ||
const returnType = translatePrimitiveJSTypeToCpp(propTypeAnnotation.returnTypeAnnotation, false, typeName => `Unsupported return type for ${prop.name}. Found: ${typeName}`, resolveAlias); // The first param will always be the runtime reference. | ||
paramTypes.unshift('jsi::Runtime &rt'); | ||
const method = `${returnType} ${prop.name}(${paramTypes.join(', ')})`; | ||
if (abstract) { | ||
return `virtual ${method} = 0;`; | ||
} | ||
return `${method} override { | ||
static_assert( | ||
bridging::getParameterCount(&T::${prop.name}) == ${paramTypes.length}, | ||
"Expected ${prop.name}(...) to have ${paramTypes.length} parameters"); | ||
return bridging::callFromJs<${returnType}>( | ||
rt, &T::${prop.name}, jsInvoker_, ${['instance_', ...params].join(', ')}); | ||
}`; | ||
} | ||
module.exports = { | ||
generate(libraryName, schema, packageName, assumeNonnull = false) { | ||
const nativeModules = getModules(schema); | ||
const modules = Object.keys(nativeModules).flatMap(hasteModuleName => { | ||
const _nativeModules$hasteM = nativeModules[hasteModuleName], | ||
aliases = _nativeModules$hasteM.aliases, | ||
properties = _nativeModules$hasteM.spec.properties, | ||
_nativeModules$hasteM2 = _slicedToArray(_nativeModules$hasteM.moduleNames, 1), | ||
moduleName = _nativeModules$hasteM2[0]; | ||
const resolveAlias = createAliasResolver(aliases); | ||
const structs = createStructs(moduleName, aliases, resolveAlias); | ||
return [ModuleClassDeclarationTemplate({ | ||
hasteModuleName, | ||
moduleProperties: properties.map(prop => translatePropertyToCpp(prop, resolveAlias, true)), | ||
structs | ||
}), ModuleSpecClassDeclarationTemplate({ | ||
hasteModuleName, | ||
moduleName, | ||
moduleProperties: properties.map(prop => translatePropertyToCpp(prop, resolveAlias)) | ||
})]; | ||
}); | ||
const fileName = `${libraryName}JSI.h`; | ||
const replacedTemplate = FileTemplate({ | ||
modules | ||
}); | ||
return new Map([[fileName, replacedTemplate]]); | ||
}, | ||
}; | ||
} | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,40 +10,34 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); } | ||
const {createAliasResolver, getModules} = require('./Utils'); | ||
const {unwrapNullable} = require('../../parsers/flow/modules/utils'); | ||
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } | ||
function _iterableToArrayLimit(arr, i) { if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } | ||
function FileTemplate( | ||
config | ||
, | ||
) { | ||
const {packageName, className, methods, imports} = config; | ||
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } | ||
const _require = require('./Utils'), | ||
createAliasResolver = _require.createAliasResolver, | ||
getModules = _require.getModules; | ||
const _require2 = require('../../parsers/parsers-commons'), | ||
unwrapNullable = _require2.unwrapNullable; | ||
function FileTemplate(config) { | ||
const packageName = config.packageName, | ||
className = config.className, | ||
methods = config.methods, | ||
imports = config.imports; | ||
return ` | ||
/** | ||
* ${'C'}opyright (c) Facebook, Inc. and its affiliates. | ||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). | ||
* | ||
* This source code is licensed under the MIT license found in the LICENSE file in the root | ||
* directory of this source tree. | ||
* Do not edit this file as changes may cause incorrect behavior and will be lost | ||
* once the code is regenerated. | ||
* | ||
* ${'@'}generated by codegen project: GenerateModuleJavaSpec.js | ||
* | ||
* @nolint | ||
* ${'@'}nolint | ||
*/ | ||
@@ -65,56 +59,40 @@ | ||
function MethodTemplate( | ||
config | ||
, | ||
) { | ||
const { | ||
abstract, | ||
methodBody, | ||
methodJavaAnnotation, | ||
methodName, | ||
translatedReturnType, | ||
traversedArgs, | ||
} = config; | ||
function MethodTemplate(config) { | ||
const abstract = config.abstract, | ||
methodBody = config.methodBody, | ||
methodJavaAnnotation = config.methodJavaAnnotation, | ||
methodName = config.methodName, | ||
translatedReturnType = config.translatedReturnType, | ||
traversedArgs = config.traversedArgs; | ||
const methodQualifier = abstract ? 'abstract ' : ''; | ||
const methodClosing = abstract | ||
? ';' | ||
: methodBody != null && methodBody.length > 0 | ||
? ` { ${methodBody} }` | ||
: ' {}'; | ||
const methodClosing = abstract ? ';' : methodBody != null && methodBody.length > 0 ? ` { ${methodBody} }` : ' {}'; | ||
return ` ${methodJavaAnnotation} | ||
public ${methodQualifier}${translatedReturnType} ${methodName}(${traversedArgs.join( | ||
', ', | ||
)})${methodClosing}`; | ||
public ${methodQualifier}${translatedReturnType} ${methodName}(${traversedArgs.join(', ')})${methodClosing}`; | ||
} | ||
function translateFunctionParamToJavaType(param, createErrorMessage, resolveAlias, imports) { | ||
const optional = param.optional, | ||
nullableTypeAnnotation = param.typeAnnotation; | ||
function translateFunctionParamToJavaType( | ||
param , | ||
createErrorMessage , | ||
resolveAlias , | ||
imports , | ||
) { | ||
const {optional, typeAnnotation: nullableTypeAnnotation} = param; | ||
const [ | ||
typeAnnotation, | ||
nullable, | ||
] = unwrapNullable (nullableTypeAnnotation); | ||
const _unwrapNullable = unwrapNullable(nullableTypeAnnotation), | ||
_unwrapNullable2 = _slicedToArray(_unwrapNullable, 2), | ||
typeAnnotation = _unwrapNullable2[0], | ||
nullable = _unwrapNullable2[1]; | ||
const isRequired = !optional && !nullable; | ||
function wrapIntoNullableIfNeeded(generatedType ) { | ||
function wrapNullable(javaType, nullableType) { | ||
if (!isRequired) { | ||
var _nullableType; | ||
imports.add('javax.annotation.Nullable'); | ||
return `@Nullable ${generatedType}`; | ||
return `@Nullable ${(_nullableType = nullableType) !== null && _nullableType !== void 0 ? _nullableType : javaType}`; | ||
} | ||
return generatedType; | ||
} | ||
return javaType; | ||
} // FIXME: support class alias for args | ||
let realTypeAnnotation = typeAnnotation; | ||
if (realTypeAnnotation.type === 'TypeAliasTypeAnnotation') { | ||
@@ -128,38 +106,74 @@ realTypeAnnotation = resolveAlias(realTypeAnnotation.name); | ||
case 'RootTag': | ||
return !isRequired ? 'Double' : 'double'; | ||
return wrapNullable('double', 'Double'); | ||
default: | ||
(realTypeAnnotation.name ); | ||
realTypeAnnotation.name; | ||
throw new Error(createErrorMessage(realTypeAnnotation.name)); | ||
} | ||
case 'StringTypeAnnotation': | ||
return wrapIntoNullableIfNeeded('String'); | ||
return wrapNullable('String'); | ||
case 'NumberTypeAnnotation': | ||
return !isRequired ? 'Double' : 'double'; | ||
return wrapNullable('double', 'Double'); | ||
case 'FloatTypeAnnotation': | ||
return !isRequired ? 'Double' : 'double'; | ||
return wrapNullable('double', 'Double'); | ||
case 'DoubleTypeAnnotation': | ||
return !isRequired ? 'Double' : 'double'; | ||
return wrapNullable('double', 'Double'); | ||
case 'Int32TypeAnnotation': | ||
return !isRequired ? 'Double' : 'double'; | ||
return wrapNullable('double', 'Double'); | ||
case 'BooleanTypeAnnotation': | ||
return !isRequired ? 'Boolean' : 'boolean'; | ||
return wrapNullable('boolean', 'Boolean'); | ||
case 'EnumDeclaration': | ||
switch (realTypeAnnotation.memberType) { | ||
case 'NumberTypeAnnotation': | ||
return wrapNullable('double', 'Double'); | ||
case 'StringTypeAnnotation': | ||
return wrapNullable('String'); | ||
default: | ||
throw new Error(createErrorMessage(realTypeAnnotation.type)); | ||
} | ||
case 'UnionTypeAnnotation': | ||
switch (typeAnnotation.memberType) { | ||
case 'NumberTypeAnnotation': | ||
return wrapNullable('double', 'Double'); | ||
case 'ObjectTypeAnnotation': | ||
imports.add('com.facebook.react.bridge.ReadableMap'); | ||
return wrapNullable('ReadableMap'); | ||
case 'StringTypeAnnotation': | ||
return wrapNullable('String'); | ||
default: | ||
throw new Error(`Unsupported union member returning value, found: ${realTypeAnnotation.memberType}"`); | ||
} | ||
case 'ObjectTypeAnnotation': | ||
imports.add('com.facebook.react.bridge.ReadableMap'); | ||
if (typeAnnotation.type === 'TypeAliasTypeAnnotation') { | ||
// No class alias for args, so it still falls under ReadableMap. | ||
return 'ReadableMap'; | ||
} | ||
return 'ReadableMap'; | ||
return wrapNullable('ReadableMap'); | ||
case 'GenericObjectTypeAnnotation': | ||
// Treat this the same as ObjectTypeAnnotation for now. | ||
imports.add('com.facebook.react.bridge.ReadableMap'); | ||
return 'ReadableMap'; | ||
return wrapNullable('ReadableMap'); | ||
case 'ArrayTypeAnnotation': | ||
imports.add('com.facebook.react.bridge.ReadableArray'); | ||
return 'ReadableArray'; | ||
return wrapNullable('ReadableArray'); | ||
case 'FunctionTypeAnnotation': | ||
imports.add('com.facebook.react.bridge.Callback'); | ||
return 'Callback'; | ||
return wrapNullable('Callback'); | ||
default: | ||
(realTypeAnnotation.type ); | ||
realTypeAnnotation.type; | ||
throw new Error(createErrorMessage(realTypeAnnotation.type)); | ||
@@ -169,24 +183,22 @@ } | ||
function translateFunctionReturnTypeToJavaType( | ||
nullableReturnTypeAnnotation , | ||
createErrorMessage , | ||
resolveAlias , | ||
imports , | ||
) { | ||
const [ | ||
returnTypeAnnotation, | ||
nullable, | ||
] = unwrapNullable ( | ||
nullableReturnTypeAnnotation, | ||
); | ||
function translateFunctionReturnTypeToJavaType(nullableReturnTypeAnnotation, createErrorMessage, resolveAlias, imports) { | ||
const _unwrapNullable3 = unwrapNullable(nullableReturnTypeAnnotation), | ||
_unwrapNullable4 = _slicedToArray(_unwrapNullable3, 2), | ||
returnTypeAnnotation = _unwrapNullable4[0], | ||
nullable = _unwrapNullable4[1]; | ||
function wrapIntoNullableIfNeeded(generatedType ) { | ||
function wrapNullable(javaType, nullableType) { | ||
if (nullable) { | ||
var _nullableType2; | ||
imports.add('javax.annotation.Nullable'); | ||
return `@Nullable ${generatedType}`; | ||
return `@Nullable ${(_nullableType2 = nullableType) !== null && _nullableType2 !== void 0 ? _nullableType2 : javaType}`; | ||
} | ||
return generatedType; | ||
} | ||
return javaType; | ||
} // FIXME: support class alias for args | ||
let realTypeAnnotation = returnTypeAnnotation; | ||
if (realTypeAnnotation.type === 'TypeAliasTypeAnnotation') { | ||
@@ -200,34 +212,75 @@ realTypeAnnotation = resolveAlias(realTypeAnnotation.name); | ||
case 'RootTag': | ||
return nullable ? 'Double' : 'double'; | ||
return wrapNullable('double', 'Double'); | ||
default: | ||
(realTypeAnnotation.name ); | ||
realTypeAnnotation.name; | ||
throw new Error(createErrorMessage(realTypeAnnotation.name)); | ||
} | ||
case 'VoidTypeAnnotation': | ||
return 'void'; | ||
case 'PromiseTypeAnnotation': | ||
return 'void'; | ||
case 'StringTypeAnnotation': | ||
return wrapIntoNullableIfNeeded('String'); | ||
return wrapNullable('String'); | ||
case 'NumberTypeAnnotation': | ||
return nullable ? 'Double' : 'double'; | ||
return wrapNullable('double', 'Double'); | ||
case 'FloatTypeAnnotation': | ||
return nullable ? 'Double' : 'double'; | ||
return wrapNullable('double', 'Double'); | ||
case 'DoubleTypeAnnotation': | ||
return nullable ? 'Double' : 'double'; | ||
return wrapNullable('double', 'Double'); | ||
case 'Int32TypeAnnotation': | ||
return nullable ? 'Double' : 'double'; | ||
return wrapNullable('double', 'Double'); | ||
case 'BooleanTypeAnnotation': | ||
return nullable ? 'Boolean' : 'boolean'; | ||
return wrapNullable('boolean', 'Boolean'); | ||
case 'EnumDeclaration': | ||
switch (realTypeAnnotation.memberType) { | ||
case 'NumberTypeAnnotation': | ||
return wrapNullable('double', 'Double'); | ||
case 'StringTypeAnnotation': | ||
return wrapNullable('String'); | ||
default: | ||
throw new Error(createErrorMessage(realTypeAnnotation.type)); | ||
} | ||
case 'UnionTypeAnnotation': | ||
switch (realTypeAnnotation.memberType) { | ||
case 'NumberTypeAnnotation': | ||
return wrapNullable('double', 'Double'); | ||
case 'ObjectTypeAnnotation': | ||
imports.add('com.facebook.react.bridge.WritableMap'); | ||
return wrapNullable('WritableMap'); | ||
case 'StringTypeAnnotation': | ||
return wrapNullable('String'); | ||
default: | ||
throw new Error(`Unsupported union member returning value, found: ${realTypeAnnotation.memberType}"`); | ||
} | ||
case 'ObjectTypeAnnotation': | ||
imports.add('com.facebook.react.bridge.WritableMap'); | ||
return 'WritableMap'; | ||
return wrapNullable('WritableMap'); | ||
case 'GenericObjectTypeAnnotation': | ||
imports.add('com.facebook.react.bridge.WritableMap'); | ||
return 'WritableMap'; | ||
return wrapNullable('WritableMap'); | ||
case 'ArrayTypeAnnotation': | ||
imports.add('com.facebook.react.bridge.WritableArray'); | ||
return 'WritableArray'; | ||
return wrapNullable('WritableArray'); | ||
default: | ||
(realTypeAnnotation.type ); | ||
realTypeAnnotation.type; | ||
throw new Error(createErrorMessage(realTypeAnnotation.type)); | ||
@@ -237,15 +290,10 @@ } | ||
function getFalsyReturnStatementFromReturnType( | ||
nullableReturnTypeAnnotation , | ||
createErrorMessage , | ||
resolveAlias , | ||
) { | ||
const [ | ||
returnTypeAnnotation, | ||
nullable, | ||
] = unwrapNullable ( | ||
nullableReturnTypeAnnotation, | ||
); | ||
function getFalsyReturnStatementFromReturnType(nullableReturnTypeAnnotation, createErrorMessage, resolveAlias) { | ||
const _unwrapNullable5 = unwrapNullable(nullableReturnTypeAnnotation), | ||
_unwrapNullable6 = _slicedToArray(_unwrapNullable5, 2), | ||
returnTypeAnnotation = _unwrapNullable6[0], | ||
nullable = _unwrapNullable6[1]; | ||
let realTypeAnnotation = returnTypeAnnotation; | ||
if (realTypeAnnotation.type === 'TypeAliasTypeAnnotation') { | ||
@@ -260,49 +308,84 @@ realTypeAnnotation = resolveAlias(realTypeAnnotation.name); | ||
return 'return 0.0;'; | ||
default: | ||
(realTypeAnnotation.name ); | ||
realTypeAnnotation.name; | ||
throw new Error(createErrorMessage(realTypeAnnotation.name)); | ||
} | ||
case 'VoidTypeAnnotation': | ||
return ''; | ||
case 'PromiseTypeAnnotation': | ||
return ''; | ||
case 'NumberTypeAnnotation': | ||
return nullable ? 'return null;' : 'return 0;'; | ||
case 'FloatTypeAnnotation': | ||
return nullable ? 'return null;' : 'return 0.0;'; | ||
case 'DoubleTypeAnnotation': | ||
return nullable ? 'return null;' : 'return 0.0;'; | ||
case 'Int32TypeAnnotation': | ||
return nullable ? 'return null;' : 'return 0;'; | ||
case 'BooleanTypeAnnotation': | ||
return nullable ? 'return null;' : 'return false;'; | ||
case 'EnumDeclaration': | ||
switch (realTypeAnnotation.memberType) { | ||
case 'NumberTypeAnnotation': | ||
return nullable ? 'return null;' : 'return 0;'; | ||
case 'StringTypeAnnotation': | ||
return nullable ? 'return null;' : 'return "";'; | ||
default: | ||
throw new Error(createErrorMessage(realTypeAnnotation.type)); | ||
} | ||
case 'UnionTypeAnnotation': | ||
switch (realTypeAnnotation.memberType) { | ||
case 'NumberTypeAnnotation': | ||
return nullable ? 'return null;' : 'return 0;'; | ||
case 'ObjectTypeAnnotation': | ||
return 'return null;'; | ||
case 'StringTypeAnnotation': | ||
return nullable ? 'return null;' : 'return "";'; | ||
default: | ||
throw new Error(`Unsupported union member returning value, found: ${realTypeAnnotation.memberType}"`); | ||
} | ||
case 'StringTypeAnnotation': | ||
return nullable ? 'return null;' : 'return "";'; | ||
case 'ObjectTypeAnnotation': | ||
return 'return null;'; | ||
case 'GenericObjectTypeAnnotation': | ||
return 'return null;'; | ||
case 'ArrayTypeAnnotation': | ||
return 'return null;'; | ||
default: | ||
(realTypeAnnotation.type ); | ||
realTypeAnnotation.type; | ||
throw new Error(createErrorMessage(realTypeAnnotation.type)); | ||
} | ||
} | ||
} // Build special-cased runtime check for getConstants(). | ||
// Build special-cased runtime check for getConstants(). | ||
function buildGetConstantsMethod( | ||
method , | ||
imports , | ||
) { | ||
const [ | ||
methodTypeAnnotation, | ||
] = unwrapNullable (method.typeAnnotation); | ||
if ( | ||
methodTypeAnnotation.returnTypeAnnotation.type === 'ObjectTypeAnnotation' | ||
) { | ||
function buildGetConstantsMethod(method, imports) { | ||
const _unwrapNullable7 = unwrapNullable(method.typeAnnotation), | ||
_unwrapNullable8 = _slicedToArray(_unwrapNullable7, 1), | ||
methodTypeAnnotation = _unwrapNullable8[0]; | ||
if (methodTypeAnnotation.returnTypeAnnotation.type === 'ObjectTypeAnnotation') { | ||
const requiredProps = []; | ||
const optionalProps = []; | ||
const rawProperties = | ||
methodTypeAnnotation.returnTypeAnnotation.properties || []; | ||
const rawProperties = methodTypeAnnotation.returnTypeAnnotation.properties || []; | ||
rawProperties.forEach(p => { | ||
@@ -315,2 +398,3 @@ if (p.optional || p.typeAnnotation.type === 'NullableTypeAnnotation') { | ||
}); | ||
if (requiredProps.length === 0 && optionalProps.length === 0) { | ||
@@ -327,22 +411,8 @@ // Nothing to validate during runtime. | ||
imports.add('javax.annotation.Nullable'); | ||
const requiredPropsFragment = | ||
requiredProps.length > 0 | ||
? `Arrays.asList( | ||
${requiredProps | ||
.sort() | ||
.map(p => `"${p}"`) | ||
.join(',\n ')} | ||
)` | ||
: ''; | ||
const optionalPropsFragment = | ||
optionalProps.length > 0 | ||
? `Arrays.asList( | ||
${optionalProps | ||
.sort() | ||
.map(p => `"${p}"`) | ||
.join(',\n ')} | ||
)` | ||
: ''; | ||
const requiredPropsFragment = requiredProps.length > 0 ? `Arrays.asList( | ||
${requiredProps.sort().map(p => `"${p}"`).join(',\n ')} | ||
)` : ''; | ||
const optionalPropsFragment = optionalProps.length > 0 ? `Arrays.asList( | ||
${optionalProps.sort().map(p => `"${p}"`).join(',\n ')} | ||
)` : ''; | ||
return ` protected abstract Map<String, Object> getTypedExportedConstants(); | ||
@@ -377,37 +447,21 @@ | ||
module.exports = { | ||
generate( | ||
libraryName , | ||
schema , | ||
packageName , | ||
assumeNonnull = false, | ||
) { | ||
generate(libraryName, schema, packageName, assumeNonnull = false) { | ||
const files = new Map(); | ||
const nativeModules = getModules(schema); | ||
const normalizedPackageName = | ||
packageName == null ? 'com.facebook.fbreact.specs' : packageName; | ||
const normalizedPackageName = packageName == null ? 'com.facebook.fbreact.specs' : packageName; | ||
const outputDir = `java/${normalizedPackageName.replace(/\./g, '/')}`; | ||
Object.keys(nativeModules).forEach(hasteModuleName => { | ||
const _nativeModules$hasteM = nativeModules[hasteModuleName], | ||
aliases = _nativeModules$hasteM.aliases, | ||
excludedPlatforms = _nativeModules$hasteM.excludedPlatforms, | ||
properties = _nativeModules$hasteM.spec.properties; | ||
Object.keys(nativeModules).forEach(hasteModuleName => { | ||
const { | ||
aliases, | ||
excludedPlatforms, | ||
spec: {properties}, | ||
} = nativeModules[hasteModuleName]; | ||
if (excludedPlatforms != null && excludedPlatforms.includes('android')) { | ||
return; | ||
} | ||
const resolveAlias = createAliasResolver(aliases); | ||
const className = `${hasteModuleName}Spec`; | ||
const imports = new Set([ | ||
// Always required. | ||
'com.facebook.react.bridge.ReactApplicationContext', | ||
'com.facebook.react.bridge.ReactContextBaseJavaModule', | ||
'com.facebook.react.bridge.ReactMethod', | ||
'com.facebook.react.bridge.ReactModuleWithSpec', | ||
'com.facebook.react.turbomodule.core.interfaces.TurboModule', | ||
'com.facebook.proguard.annotations.DoNotStrip', | ||
]); | ||
const imports = new Set([// Always required. | ||
'com.facebook.react.bridge.ReactApplicationContext', 'com.facebook.react.bridge.ReactContextBaseJavaModule', 'com.facebook.react.bridge.ReactMethod', 'com.facebook.react.bridge.ReactModuleWithSpec', 'com.facebook.react.turbomodule.core.interfaces.TurboModule', 'com.facebook.proguard.annotations.DoNotStrip']); | ||
const methods = properties.map(method => { | ||
@@ -418,32 +472,13 @@ if (method.name === 'getConstants') { | ||
const [ | ||
methodTypeAnnotation, | ||
] = unwrapNullable ( | ||
method.typeAnnotation, | ||
); | ||
const _unwrapNullable9 = unwrapNullable(method.typeAnnotation), | ||
_unwrapNullable10 = _slicedToArray(_unwrapNullable9, 1), | ||
methodTypeAnnotation = _unwrapNullable10[0]; // Handle return type | ||
// Handle return type | ||
const translatedReturnType = translateFunctionReturnTypeToJavaType( | ||
methodTypeAnnotation.returnTypeAnnotation, | ||
typeName => | ||
`Unsupported return type for method ${method.name}. Found: ${typeName}`, | ||
resolveAlias, | ||
imports, | ||
); | ||
const returningPromise = | ||
methodTypeAnnotation.returnTypeAnnotation.type === | ||
'PromiseTypeAnnotation'; | ||
const isSyncMethod = | ||
methodTypeAnnotation.returnTypeAnnotation.type !== | ||
'VoidTypeAnnotation' && !returningPromise; | ||
// Handle method args | ||
const translatedReturnType = translateFunctionReturnTypeToJavaType(methodTypeAnnotation.returnTypeAnnotation, typeName => `Unsupported return type for method ${method.name}. Found: ${typeName}`, resolveAlias, imports); | ||
const returningPromise = methodTypeAnnotation.returnTypeAnnotation.type === 'PromiseTypeAnnotation'; | ||
const isSyncMethod = methodTypeAnnotation.returnTypeAnnotation.type !== 'VoidTypeAnnotation' && !returningPromise; // Handle method args | ||
const traversedArgs = methodTypeAnnotation.params.map(param => { | ||
const translatedParam = translateFunctionParamToJavaType( | ||
param, | ||
typeName => | ||
`Unsupported type for param "${param.name}" in ${method.name}. Found: ${typeName}`, | ||
resolveAlias, | ||
imports, | ||
); | ||
const translatedParam = translateFunctionParamToJavaType(param, typeName => `Unsupported type for param "${param.name}" in ${method.name}. Found: ${typeName}`, resolveAlias, imports); | ||
return `${translatedParam} ${param.name}`; | ||
@@ -458,13 +493,4 @@ }); | ||
const methodJavaAnnotation = `@ReactMethod${ | ||
isSyncMethod ? '(isBlockingSynchronousMethod = true)' : '' | ||
}\n @DoNotStrip`; | ||
const methodBody = method.optional | ||
? getFalsyReturnStatementFromReturnType( | ||
methodTypeAnnotation.returnTypeAnnotation, | ||
typeName => | ||
`Cannot build falsy return statement for return type for method ${method.name}. Found: ${typeName}`, | ||
resolveAlias, | ||
) | ||
: null; | ||
const methodJavaAnnotation = `@ReactMethod${isSyncMethod ? '(isBlockingSynchronousMethod = true)' : ''}\n @DoNotStrip`; | ||
const methodBody = method.optional ? getFalsyReturnStatementFromReturnType(methodTypeAnnotation.returnTypeAnnotation, typeName => `Cannot build falsy return statement for return type for method ${method.name}. Found: ${typeName}`, resolveAlias) : null; | ||
return MethodTemplate({ | ||
@@ -476,22 +502,15 @@ abstract: !method.optional, | ||
translatedReturnType, | ||
traversedArgs, | ||
traversedArgs | ||
}); | ||
}); | ||
files.set( | ||
`${outputDir}/${className}.java`, | ||
FileTemplate({ | ||
packageName: normalizedPackageName, | ||
className, | ||
methods: methods.filter(Boolean).join('\n\n'), | ||
imports: Array.from(imports) | ||
.sort() | ||
.map(p => `import ${p};`) | ||
.join('\n'), | ||
}), | ||
); | ||
files.set(`${outputDir}/${className}.java`, FileTemplate({ | ||
packageName: normalizedPackageName, | ||
className, | ||
methods: methods.filter(Boolean).join('\n\n'), | ||
imports: Array.from(imports).sort().map(p => `import ${p};`).join('\n') | ||
})); | ||
}); | ||
return files; | ||
} | ||
return files; | ||
}, | ||
}; | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,30 +10,19 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); } | ||
const {createAliasResolver, getModules} = require('./Utils'); | ||
const {unwrapNullable} = require('../../parsers/flow/modules/utils'); | ||
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } | ||
function _iterableToArrayLimit(arr, i) { if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } | ||
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } | ||
const _require = require('./Utils'), | ||
createAliasResolver = _require.createAliasResolver, | ||
getModules = _require.getModules; | ||
const _require2 = require('../../parsers/parsers-commons'), | ||
unwrapNullable = _require2.unwrapNullable; | ||
const HostFunctionTemplate = ({ | ||
@@ -43,11 +32,7 @@ hasteModuleName, | ||
jniSignature, | ||
jsReturnType, | ||
} | ||
) => { | ||
jsReturnType | ||
}) => { | ||
return `static facebook::jsi::Value __hostFunction_${hasteModuleName}SpecJSI_${propertyName}(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { | ||
return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, ${jsReturnType}, "${propertyName}", "${jniSignature}", args, count); | ||
static jmethodID cachedMethodId = nullptr; | ||
return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, ${jsReturnType}, "${propertyName}", "${jniSignature}", args, count, cachedMethodId); | ||
}`; | ||
@@ -58,18 +43,13 @@ }; | ||
hasteModuleName, | ||
methods, | ||
} | ||
) => { | ||
methods | ||
}) => { | ||
return ` | ||
${hasteModuleName}SpecJSI::${hasteModuleName}SpecJSI(const JavaTurboModule::InitParams ¶ms) | ||
: JavaTurboModule(params) { | ||
${methods | ||
.map(({propertyName, argCount}) => { | ||
${methods.map(({ | ||
propertyName, | ||
argCount | ||
}) => { | ||
return ` methodMap_["${propertyName}"] = MethodMetadata {${argCount}, __hostFunction_${hasteModuleName}SpecJSI_${propertyName}};`; | ||
}) | ||
.join('\n')} | ||
}).join('\n')} | ||
}`.trim(); | ||
@@ -80,4 +60,4 @@ }; | ||
moduleName, | ||
hasteModuleName, | ||
} ) => { | ||
hasteModuleName | ||
}) => { | ||
return ` if (moduleName == "${moduleName}") { | ||
@@ -92,20 +72,10 @@ return std::make_shared<${hasteModuleName}SpecJSI>(params); | ||
modules, | ||
moduleLookups, | ||
} | ||
) => { | ||
moduleLookups | ||
}) => { | ||
return ` | ||
/** | ||
* ${'C'}opyright (c) Facebook, Inc. and its affiliates. | ||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* Do not edit this file as changes may cause incorrect behavior and will be lost | ||
* once the code is regenerated. | ||
* | ||
@@ -122,3 +92,3 @@ * ${'@'}generated by codegen project: GenerateModuleJniCpp.js | ||
std::shared_ptr<TurboModule> ${libraryName}_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms) { | ||
std::shared_ptr<TurboModule> ${libraryName}_ModuleProvider(const std::string &moduleName, const JavaTurboModule::InitParams ¶ms) { | ||
${moduleLookups.map(ModuleLookupTemplate).join('\n')} | ||
@@ -133,10 +103,9 @@ return nullptr; | ||
function translateReturnTypeToKind( | ||
nullableTypeAnnotation , | ||
resolveAlias , | ||
) { | ||
const [typeAnnotation] = unwrapNullable ( | ||
nullableTypeAnnotation, | ||
); | ||
function translateReturnTypeToKind(nullableTypeAnnotation, resolveAlias) { | ||
const _unwrapNullable = unwrapNullable(nullableTypeAnnotation), | ||
_unwrapNullable2 = _slicedToArray(_unwrapNullable, 1), | ||
typeAnnotation = _unwrapNullable2[0]; | ||
let realTypeAnnotation = typeAnnotation; | ||
if (realTypeAnnotation.type === 'TypeAliasTypeAnnotation') { | ||
@@ -151,52 +120,86 @@ realTypeAnnotation = resolveAlias(realTypeAnnotation.name); | ||
return 'NumberKind'; | ||
default: | ||
(realTypeAnnotation.name ); | ||
throw new Error( | ||
`Invalid ReservedFunctionValueTypeName name, got ${realTypeAnnotation.name}`, | ||
); | ||
realTypeAnnotation.name; | ||
throw new Error(`Invalid ReservedFunctionValueTypeName name, got ${realTypeAnnotation.name}`); | ||
} | ||
case 'VoidTypeAnnotation': | ||
return 'VoidKind'; | ||
case 'StringTypeAnnotation': | ||
return 'StringKind'; | ||
case 'BooleanTypeAnnotation': | ||
return 'BooleanKind'; | ||
case 'EnumDeclaration': | ||
switch (typeAnnotation.memberType) { | ||
case 'NumberTypeAnnotation': | ||
return 'NumberKind'; | ||
case 'StringTypeAnnotation': | ||
return 'StringKind'; | ||
default: | ||
throw new Error(`Unknown enum prop type for returning value, found: ${realTypeAnnotation.type}"`); | ||
} | ||
case 'UnionTypeAnnotation': | ||
switch (typeAnnotation.memberType) { | ||
case 'NumberTypeAnnotation': | ||
return 'NumberKind'; | ||
case 'ObjectTypeAnnotation': | ||
return 'ObjectKind'; | ||
case 'StringTypeAnnotation': | ||
return 'StringKind'; | ||
default: | ||
throw new Error(`Unsupported union member returning value, found: ${realTypeAnnotation.memberType}"`); | ||
} | ||
case 'NumberTypeAnnotation': | ||
return 'NumberKind'; | ||
case 'DoubleTypeAnnotation': | ||
return 'NumberKind'; | ||
case 'FloatTypeAnnotation': | ||
return 'NumberKind'; | ||
case 'Int32TypeAnnotation': | ||
return 'NumberKind'; | ||
case 'PromiseTypeAnnotation': | ||
return 'PromiseKind'; | ||
case 'GenericObjectTypeAnnotation': | ||
return 'ObjectKind'; | ||
case 'ObjectTypeAnnotation': | ||
return 'ObjectKind'; | ||
case 'ArrayTypeAnnotation': | ||
return 'ArrayKind'; | ||
default: | ||
(realTypeAnnotation.type ); | ||
throw new Error( | ||
`Unknown prop type for returning value, found: ${realTypeAnnotation.type}"`, | ||
); | ||
realTypeAnnotation.type; | ||
throw new Error(`Unknown prop type for returning value, found: ${realTypeAnnotation.type}"`); | ||
} | ||
} | ||
function translateParamTypeToJniType(param, resolveAlias) { | ||
const optional = param.optional, | ||
nullableTypeAnnotation = param.typeAnnotation; | ||
function translateParamTypeToJniType( | ||
param , | ||
resolveAlias , | ||
) { | ||
const {optional, typeAnnotation: nullableTypeAnnotation} = param; | ||
const [ | ||
typeAnnotation, | ||
nullable, | ||
] = unwrapNullable (nullableTypeAnnotation); | ||
const _unwrapNullable3 = unwrapNullable(nullableTypeAnnotation), | ||
_unwrapNullable4 = _slicedToArray(_unwrapNullable3, 2), | ||
typeAnnotation = _unwrapNullable4[0], | ||
nullable = _unwrapNullable4[1]; | ||
const isRequired = !optional && !nullable; | ||
let realTypeAnnotation = typeAnnotation; | ||
let realTypeAnnotation = typeAnnotation; | ||
if (realTypeAnnotation.type === 'TypeAliasTypeAnnotation') { | ||
@@ -211,43 +214,79 @@ realTypeAnnotation = resolveAlias(realTypeAnnotation.name); | ||
return !isRequired ? 'Ljava/lang/Double;' : 'D'; | ||
default: | ||
(realTypeAnnotation.name ); | ||
throw new Error( | ||
`Invalid ReservedFunctionValueTypeName name, got ${realTypeAnnotation.name}`, | ||
); | ||
realTypeAnnotation.name; | ||
throw new Error(`Invalid ReservedFunctionValueTypeName name, got ${realTypeAnnotation.name}`); | ||
} | ||
case 'StringTypeAnnotation': | ||
return 'Ljava/lang/String;'; | ||
case 'BooleanTypeAnnotation': | ||
return !isRequired ? 'Ljava/lang/Boolean;' : 'Z'; | ||
case 'EnumDeclaration': | ||
switch (typeAnnotation.memberType) { | ||
case 'NumberTypeAnnotation': | ||
return !isRequired ? 'Ljava/lang/Double;' : 'D'; | ||
case 'StringTypeAnnotation': | ||
return 'Ljava/lang/String;'; | ||
default: | ||
throw new Error(`Unknown enum prop type for method arg, found: ${realTypeAnnotation.type}"`); | ||
} | ||
case 'UnionTypeAnnotation': | ||
switch (typeAnnotation.memberType) { | ||
case 'NumberTypeAnnotation': | ||
return !isRequired ? 'Ljava/lang/Double;' : 'D'; | ||
case 'ObjectTypeAnnotation': | ||
return 'Lcom/facebook/react/bridge/ReadableMap;'; | ||
case 'StringTypeAnnotation': | ||
return 'Ljava/lang/String;'; | ||
default: | ||
throw new Error(`Unsupported union prop value, found: ${realTypeAnnotation.memberType}"`); | ||
} | ||
case 'NumberTypeAnnotation': | ||
return !isRequired ? 'Ljava/lang/Double;' : 'D'; | ||
case 'DoubleTypeAnnotation': | ||
return !isRequired ? 'Ljava/lang/Double;' : 'D'; | ||
case 'FloatTypeAnnotation': | ||
return !isRequired ? 'Ljava/lang/Double;' : 'D'; | ||
case 'Int32TypeAnnotation': | ||
return !isRequired ? 'Ljava/lang/Double;' : 'D'; | ||
case 'GenericObjectTypeAnnotation': | ||
return 'Lcom/facebook/react/bridge/ReadableMap;'; | ||
case 'ObjectTypeAnnotation': | ||
return 'Lcom/facebook/react/bridge/ReadableMap;'; | ||
case 'ArrayTypeAnnotation': | ||
return 'Lcom/facebook/react/bridge/ReadableArray;'; | ||
case 'FunctionTypeAnnotation': | ||
return 'Lcom/facebook/react/bridge/Callback;'; | ||
default: | ||
(realTypeAnnotation.type ); | ||
throw new Error( | ||
`Unknown prop type for method arg, found: ${realTypeAnnotation.type}"`, | ||
); | ||
realTypeAnnotation.type; | ||
throw new Error(`Unknown prop type for method arg, found: ${realTypeAnnotation.type}"`); | ||
} | ||
} | ||
function translateReturnTypeToJniType( | ||
nullableTypeAnnotation , | ||
resolveAlias , | ||
) { | ||
const [typeAnnotation, nullable] = unwrapNullable(nullableTypeAnnotation); | ||
function translateReturnTypeToJniType(nullableTypeAnnotation, resolveAlias) { | ||
const _unwrapNullable5 = unwrapNullable(nullableTypeAnnotation), | ||
_unwrapNullable6 = _slicedToArray(_unwrapNullable5, 2), | ||
typeAnnotation = _unwrapNullable6[0], | ||
nullable = _unwrapNullable6[1]; | ||
let realTypeAnnotation = typeAnnotation; | ||
if (realTypeAnnotation.type === 'TypeAliasTypeAnnotation') { | ||
@@ -262,53 +301,91 @@ realTypeAnnotation = resolveAlias(realTypeAnnotation.name); | ||
return nullable ? 'Ljava/lang/Double;' : 'D'; | ||
default: | ||
(realTypeAnnotation.name ); | ||
throw new Error( | ||
`Invalid ReservedFunctionValueTypeName name, got ${realTypeAnnotation.name}`, | ||
); | ||
realTypeAnnotation.name; | ||
throw new Error(`Invalid ReservedFunctionValueTypeName name, got ${realTypeAnnotation.name}`); | ||
} | ||
case 'VoidTypeAnnotation': | ||
return 'V'; | ||
case 'StringTypeAnnotation': | ||
return 'Ljava/lang/String;'; | ||
case 'BooleanTypeAnnotation': | ||
return nullable ? 'Ljava/lang/Boolean;' : 'Z'; | ||
case 'EnumDeclaration': | ||
switch (typeAnnotation.memberType) { | ||
case 'NumberTypeAnnotation': | ||
return nullable ? 'Ljava/lang/Double;' : 'D'; | ||
case 'StringTypeAnnotation': | ||
return 'Ljava/lang/String;'; | ||
default: | ||
throw new Error(`Unknown enum prop type for method return type, found: ${realTypeAnnotation.type}"`); | ||
} | ||
case 'UnionTypeAnnotation': | ||
switch (typeAnnotation.memberType) { | ||
case 'NumberTypeAnnotation': | ||
return nullable ? 'Ljava/lang/Double;' : 'D'; | ||
case 'ObjectTypeAnnotation': | ||
return 'Lcom/facebook/react/bridge/WritableMap;'; | ||
case 'StringTypeAnnotation': | ||
return 'Ljava/lang/String;'; | ||
default: | ||
throw new Error(`Unsupported union member type, found: ${realTypeAnnotation.memberType}"`); | ||
} | ||
case 'NumberTypeAnnotation': | ||
return nullable ? 'Ljava/lang/Double;' : 'D'; | ||
case 'DoubleTypeAnnotation': | ||
return nullable ? 'Ljava/lang/Double;' : 'D'; | ||
case 'FloatTypeAnnotation': | ||
return nullable ? 'Ljava/lang/Double;' : 'D'; | ||
case 'Int32TypeAnnotation': | ||
return nullable ? 'Ljava/lang/Double;' : 'D'; | ||
case 'PromiseTypeAnnotation': | ||
return 'Lcom/facebook/react/bridge/Promise;'; | ||
case 'GenericObjectTypeAnnotation': | ||
return 'Lcom/facebook/react/bridge/WritableMap;'; | ||
case 'ObjectTypeAnnotation': | ||
return 'Lcom/facebook/react/bridge/WritableMap;'; | ||
case 'ArrayTypeAnnotation': | ||
return 'Lcom/facebook/react/bridge/WritableArray;'; | ||
default: | ||
(realTypeAnnotation.type ); | ||
throw new Error( | ||
`Unknown prop type for method return type, found: ${realTypeAnnotation.type}"`, | ||
); | ||
realTypeAnnotation.type; | ||
throw new Error(`Unknown prop type for method return type, found: ${realTypeAnnotation.type}"`); | ||
} | ||
} | ||
function translateMethodTypeToJniSignature( | ||
property , | ||
resolveAlias , | ||
) { | ||
const {name, typeAnnotation} = property; | ||
let [ | ||
{returnTypeAnnotation, params}, | ||
] = unwrapNullable (typeAnnotation); | ||
function translateMethodTypeToJniSignature(property, resolveAlias) { | ||
const name = property.name, | ||
typeAnnotation = property.typeAnnotation; | ||
let _unwrapNullable7 = unwrapNullable(typeAnnotation), | ||
_unwrapNullable8 = _slicedToArray(_unwrapNullable7, 1), | ||
_unwrapNullable8$ = _unwrapNullable8[0], | ||
returnTypeAnnotation = _unwrapNullable8$.returnTypeAnnotation, | ||
params = _unwrapNullable8$.params; | ||
params = [...params]; | ||
let processedReturnTypeAnnotation = returnTypeAnnotation; | ||
const isPromiseReturn = returnTypeAnnotation.type === 'PromiseTypeAnnotation'; | ||
if (isPromiseReturn) { | ||
processedReturnTypeAnnotation = { | ||
type: 'VoidTypeAnnotation', | ||
type: 'VoidTypeAnnotation' | ||
}; | ||
@@ -320,37 +397,21 @@ } | ||
}); | ||
if (isPromiseReturn) { | ||
// Additional promise arg for this case. | ||
argsSignatureParts.push( | ||
translateReturnTypeToJniType(returnTypeAnnotation, resolveAlias), | ||
); | ||
argsSignatureParts.push(translateReturnTypeToJniType(returnTypeAnnotation, resolveAlias)); | ||
} | ||
const argsSignature = argsSignatureParts.join(''); | ||
const returnSignature = | ||
name === 'getConstants' | ||
? 'Ljava/util/Map;' | ||
: translateReturnTypeToJniType( | ||
processedReturnTypeAnnotation, | ||
resolveAlias, | ||
); | ||
const returnSignature = name === 'getConstants' ? 'Ljava/util/Map;' : translateReturnTypeToJniType(processedReturnTypeAnnotation, resolveAlias); | ||
return `(${argsSignature})${returnSignature}`; | ||
} | ||
function translateMethodForImplementation( | ||
hasteModuleName , | ||
property , | ||
resolveAlias , | ||
) { | ||
const [ | ||
propertyTypeAnnotation, | ||
] = unwrapNullable ( | ||
property.typeAnnotation, | ||
); | ||
const {returnTypeAnnotation} = propertyTypeAnnotation; | ||
function translateMethodForImplementation(hasteModuleName, property, resolveAlias) { | ||
const _unwrapNullable9 = unwrapNullable(property.typeAnnotation), | ||
_unwrapNullable10 = _slicedToArray(_unwrapNullable9, 1), | ||
propertyTypeAnnotation = _unwrapNullable10[0]; | ||
if ( | ||
property.name === 'getConstants' && | ||
returnTypeAnnotation.type === 'ObjectTypeAnnotation' && | ||
returnTypeAnnotation.properties.length === 0 | ||
) { | ||
const returnTypeAnnotation = propertyTypeAnnotation.returnTypeAnnotation; | ||
if (property.name === 'getConstants' && returnTypeAnnotation.type === 'ObjectTypeAnnotation' && returnTypeAnnotation.properties.length === 0) { | ||
return ''; | ||
@@ -363,3 +424,3 @@ } | ||
jniSignature: translateMethodTypeToJniSignature(property, resolveAlias), | ||
jsReturnType: translateReturnTypeToKind(returnTypeAnnotation, resolveAlias), | ||
jsReturnType: translateReturnTypeToKind(returnTypeAnnotation, resolveAlias) | ||
}); | ||
@@ -369,100 +430,60 @@ } | ||
module.exports = { | ||
generate( | ||
libraryName , | ||
schema , | ||
packageName , | ||
assumeNonnull = false, | ||
) { | ||
generate(libraryName, schema, packageName, assumeNonnull = false) { | ||
const nativeModules = getModules(schema); | ||
const modules = Object.keys(nativeModules).filter(hasteModuleName => { | ||
const module = nativeModules[hasteModuleName]; | ||
return !(module.excludedPlatforms != null && module.excludedPlatforms.includes('android')); | ||
}).sort().map(hasteModuleName => { | ||
const _nativeModules$hasteM = nativeModules[hasteModuleName], | ||
aliases = _nativeModules$hasteM.aliases, | ||
properties = _nativeModules$hasteM.spec.properties; | ||
const resolveAlias = createAliasResolver(aliases); | ||
const translatedMethods = properties.map(property => translateMethodForImplementation(hasteModuleName, property, resolveAlias)).join('\n\n'); | ||
return translatedMethods + '\n\n' + ModuleClassConstructorTemplate({ | ||
hasteModuleName, | ||
methods: properties.map(({ | ||
name: propertyName, | ||
typeAnnotation | ||
}) => { | ||
const _unwrapNullable11 = unwrapNullable(typeAnnotation), | ||
_unwrapNullable12 = _slicedToArray(_unwrapNullable11, 1), | ||
_unwrapNullable12$ = _unwrapNullable12[0], | ||
returnTypeAnnotation = _unwrapNullable12$.returnTypeAnnotation, | ||
params = _unwrapNullable12$.params; | ||
const modules = Object.keys(nativeModules) | ||
.filter(hasteModuleName => { | ||
const module = nativeModules[hasteModuleName]; | ||
return !( | ||
module.excludedPlatforms != null && | ||
module.excludedPlatforms.includes('android') | ||
); | ||
}) | ||
.sort() | ||
.map(hasteModuleName => { | ||
const { | ||
aliases, | ||
spec: {properties}, | ||
} = nativeModules[hasteModuleName]; | ||
const resolveAlias = createAliasResolver(aliases); | ||
if (propertyName === 'getConstants' && returnTypeAnnotation.type === 'ObjectTypeAnnotation' && returnTypeAnnotation.properties && returnTypeAnnotation.properties.length === 0) { | ||
return null; | ||
} | ||
const translatedMethods = properties | ||
.map(property => | ||
translateMethodForImplementation( | ||
hasteModuleName, | ||
property, | ||
resolveAlias, | ||
), | ||
) | ||
.join('\n\n'); | ||
return { | ||
propertyName, | ||
argCount: params.length | ||
}; | ||
}).filter(Boolean) | ||
}); | ||
}).join('\n'); // $FlowFixMe[missing-type-arg] | ||
return ( | ||
translatedMethods + | ||
'\n\n' + | ||
ModuleClassConstructorTemplate({ | ||
hasteModuleName, | ||
methods: properties | ||
.map(({name: propertyName, typeAnnotation}) => { | ||
const [ | ||
{returnTypeAnnotation, params}, | ||
] = unwrapNullable ( | ||
typeAnnotation, | ||
); | ||
const moduleLookups = Object.keys(nativeModules).filter(hasteModuleName => { | ||
const module = nativeModules[hasteModuleName]; | ||
return !(module.excludedPlatforms != null && module.excludedPlatforms.includes('android')); | ||
}).sort((a, b) => { | ||
const moduleA = nativeModules[a]; | ||
const moduleB = nativeModules[b]; | ||
const nameA = moduleA.moduleNames[0]; | ||
const nameB = moduleB.moduleNames[0]; | ||
if ( | ||
propertyName === 'getConstants' && | ||
returnTypeAnnotation.type === 'ObjectTypeAnnotation' && | ||
returnTypeAnnotation.properties && | ||
returnTypeAnnotation.properties.length === 0 | ||
) { | ||
return null; | ||
} | ||
if (nameA < nameB) { | ||
return -1; | ||
} else if (nameA > nameB) { | ||
return 1; | ||
} | ||
return { | ||
propertyName, | ||
argCount: params.length, | ||
}; | ||
}) | ||
.filter(Boolean), | ||
}) | ||
); | ||
}) | ||
.join('\n'); | ||
// $FlowFixMe[missing-type-arg] | ||
const moduleLookups = Object.keys(nativeModules) | ||
.filter(hasteModuleName => { | ||
const module = nativeModules[hasteModuleName]; | ||
return !( | ||
module.excludedPlatforms != null && | ||
module.excludedPlatforms.includes('android') | ||
); | ||
}) | ||
.sort((a, b) => { | ||
const moduleA = nativeModules[a]; | ||
const moduleB = nativeModules[b]; | ||
const nameA = moduleA.moduleNames[0]; | ||
const nameB = moduleB.moduleNames[0]; | ||
if (nameA < nameB) { | ||
return -1; | ||
} else if (nameA > nameB) { | ||
return 1; | ||
} | ||
return 0; | ||
}) | ||
.flatMap ( | ||
(hasteModuleName ) => { | ||
const {moduleNames} = nativeModules[hasteModuleName]; | ||
return moduleNames.map(moduleName => ({ | ||
moduleName, | ||
hasteModuleName, | ||
})); | ||
}, | ||
); | ||
return 0; | ||
}).flatMap(hasteModuleName => { | ||
const moduleNames = nativeModules[hasteModuleName].moduleNames; | ||
return moduleNames.map(moduleName => ({ | ||
moduleName, | ||
hasteModuleName | ||
})); | ||
}); | ||
const fileName = `${libraryName}-generated.cpp`; | ||
@@ -473,6 +494,7 @@ const replacedTemplate = FileTemplate({ | ||
moduleLookups, | ||
include: `"${libraryName}.h"`, | ||
include: `"${libraryName}.h"` | ||
}); | ||
return new Map([[`jni/${fileName}`, replacedTemplate]]); | ||
}, | ||
}; | ||
} | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,14 +10,10 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
const _require = require('./Utils'), | ||
getModules = _require.getModules; | ||
const {getModules} = require('./Utils'); | ||
const ModuleClassDeclarationTemplate = ({ | ||
hasteModuleName, | ||
} ) => { | ||
hasteModuleName | ||
}) => { | ||
return `/** | ||
@@ -35,10 +31,10 @@ * JNI C++ class for module '${hasteModuleName}' | ||
modules, | ||
libraryName, | ||
} ) => { | ||
libraryName | ||
}) => { | ||
return ` | ||
/** | ||
* ${'C'}opyright (c) Facebook, Inc. and its affiliates. | ||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* Do not edit this file as changes may cause incorrect behavior and will be lost | ||
* once the code is regenerated. | ||
* | ||
@@ -59,3 +55,4 @@ * ${'@'}generated by codegen project: GenerateModuleJniH.js | ||
std::shared_ptr<TurboModule> ${libraryName}_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms); | ||
JSI_EXPORT | ||
std::shared_ptr<TurboModule> ${libraryName}_ModuleProvider(const std::string &moduleName, const JavaTurboModule::InitParams ¶ms); | ||
@@ -65,7 +62,9 @@ } // namespace react | ||
`; | ||
}; | ||
}; // Note: this Android.mk template includes dependencies for both NativeModule and components. | ||
// Note: this Android.mk template includes dependencies for both NativeModule and components. | ||
const AndroidMkTemplate = ({libraryName} ) => { | ||
return `# Copyright (c) Facebook, Inc. and its affiliates. | ||
const AndroidMkTemplate = ({ | ||
libraryName | ||
}) => { | ||
return `# Copyright (c) Meta Platforms, Inc. and affiliates. | ||
# | ||
@@ -84,53 +83,106 @@ # This source code is licensed under the MIT license found in the | ||
LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) $(wildcard $(LOCAL_PATH)/react/renderer/components/${libraryName}/*.cpp) | ||
LOCAL_SRC_FILES := $(subst $(LOCAL_PATH)/,,$(LOCAL_SRC_FILES)) | ||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) $(LOCAL_PATH)/react/renderer/components/${libraryName} | ||
LOCAL_SHARED_LIBRARIES := libglog libfolly_json libyoga libreact_nativemodule_core librrc_view libreact_render_core libreact_render_graphics libreact_debug libreact_render_debug | ||
LOCAL_SHARED_LIBRARIES := libfbjni \ | ||
libfolly_runtime \ | ||
libglog \ | ||
libjsi \ | ||
libreact_codegen_rncore \ | ||
libreact_debug \ | ||
libreact_nativemodule_core \ | ||
libreact_render_core \ | ||
libreact_render_debug \ | ||
libreact_render_graphics \ | ||
libreact_render_imagemanager \ | ||
libreact_render_mapbuffer \ | ||
librrc_image \ | ||
librrc_view \ | ||
libturbomodulejsijni \ | ||
libyoga | ||
LOCAL_STATIC_LIBRARIES := libjsi | ||
LOCAL_CFLAGS := \\ | ||
-DLOG_TAG=\\"ReactNative\\" | ||
LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall | ||
LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall | ||
include $(BUILD_SHARED_LIBRARY) | ||
`; | ||
}; // Note: this CMakeLists.txt template includes dependencies for both NativeModule and components. | ||
const CMakeListsTemplate = ({ | ||
libraryName | ||
}) => { | ||
return `# 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. | ||
cmake_minimum_required(VERSION 3.13) | ||
set(CMAKE_VERBOSE_MAKEFILE on) | ||
file(GLOB react_codegen_SRCS CONFIGURE_DEPENDS *.cpp react/renderer/components/${libraryName}/*.cpp) | ||
add_library( | ||
react_codegen_${libraryName} | ||
SHARED | ||
\${react_codegen_SRCS} | ||
) | ||
target_include_directories(react_codegen_${libraryName} PUBLIC . react/renderer/components/${libraryName}) | ||
target_link_libraries( | ||
react_codegen_${libraryName} | ||
fbjni | ||
folly_runtime | ||
glog | ||
jsi | ||
${libraryName !== 'rncore' ? 'react_codegen_rncore' : ''} | ||
react_debug | ||
react_nativemodule_core | ||
react_render_core | ||
react_render_debug | ||
react_render_graphics | ||
react_render_imagemanager | ||
rrc_image | ||
rrc_view | ||
turbomodulejsijni | ||
yoga | ||
) | ||
target_compile_options( | ||
react_codegen_${libraryName} | ||
PRIVATE | ||
-DLOG_TAG=\\"ReactNative\\" | ||
-fexceptions | ||
-frtti | ||
-std=c++17 | ||
-Wall | ||
) | ||
`; | ||
}; | ||
module.exports = { | ||
generate( | ||
libraryName , | ||
schema , | ||
packageName , | ||
assumeNonnull = false, | ||
) { | ||
generate(libraryName, schema, packageName, assumeNonnull = false) { | ||
const nativeModules = getModules(schema); | ||
const modules = Object.keys(nativeModules) | ||
.filter(hasteModuleName => { | ||
const module = nativeModules[hasteModuleName]; | ||
return !( | ||
module.excludedPlatforms != null && | ||
module.excludedPlatforms.includes('android') | ||
); | ||
}) | ||
.sort() | ||
.map(hasteModuleName => ModuleClassDeclarationTemplate({hasteModuleName})) | ||
.join('\n'); | ||
const modules = Object.keys(nativeModules).filter(hasteModuleName => { | ||
const module = nativeModules[hasteModuleName]; | ||
return !(module.excludedPlatforms != null && module.excludedPlatforms.includes('android')); | ||
}).sort().map(hasteModuleName => ModuleClassDeclarationTemplate({ | ||
hasteModuleName | ||
})).join('\n'); | ||
const fileName = `${libraryName}.h`; | ||
const replacedTemplate = HeaderFileTemplate({ | ||
modules: modules, | ||
libraryName: libraryName.replace(/-/g, '_'), | ||
libraryName: libraryName.replace(/-/g, '_') | ||
}); | ||
return new Map([ | ||
[`jni/${fileName}`, replacedTemplate], | ||
[ | ||
'jni/Android.mk', | ||
AndroidMkTemplate({ | ||
libraryName: `${libraryName.toLowerCase()}`, | ||
}), | ||
], | ||
]); | ||
}, | ||
}; | ||
return new Map([[`jni/${fileName}`, replacedTemplate], ['jni/Android.mk', AndroidMkTemplate({ | ||
libraryName: libraryName | ||
})], ['jni/CMakeLists.txt', CMakeListsTemplate({ | ||
libraryName: libraryName | ||
})]]); | ||
} | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,23 +10,27 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
const {getSafePropertyName, getNamespacedStructName} = require('../Utils'); | ||
const {capitalize} = require('../../../Utils'); | ||
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); } | ||
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } | ||
const {unwrapNullable} = require('../../../../parsers/flow/modules/utils'); | ||
function _iterableToArrayLimit(arr, i) { if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } | ||
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } | ||
const _require = require('../Utils'), | ||
getSafePropertyName = _require.getSafePropertyName, | ||
getNamespacedStructName = _require.getNamespacedStructName; | ||
const _require2 = require('../../../Utils'), | ||
capitalize = _require2.capitalize; | ||
const _require3 = require('../../../../parsers/parsers-commons'), | ||
unwrapNullable = _require3.unwrapNullable; | ||
const StructTemplate = ({ | ||
hasteModuleName, | ||
structName, | ||
builderInputProps, | ||
} | ||
) => `namespace JS { | ||
builderInputProps | ||
}) => `namespace JS { | ||
namespace ${hasteModuleName} { | ||
@@ -62,8 +66,4 @@ struct ${structName} { | ||
structName, | ||
properties, | ||
} | ||
) => `inline JS::${hasteModuleName}::${structName}::Builder::Builder(const Input i) : _factory(^{ | ||
properties | ||
}) => `inline JS::${hasteModuleName}::${structName}::Builder::Builder(const Input i) : _factory(^{ | ||
NSMutableDictionary *d = [NSMutableDictionary new]; | ||
@@ -77,11 +77,12 @@ ${properties} | ||
function toObjCType( | ||
hasteModuleName , | ||
nullableTypeAnnotation , | ||
isOptional = false, | ||
) { | ||
const [typeAnnotation, nullable] = unwrapNullable(nullableTypeAnnotation); | ||
function toObjCType(hasteModuleName, nullableTypeAnnotation, isOptional = false) { | ||
const _unwrapNullable = unwrapNullable(nullableTypeAnnotation), | ||
_unwrapNullable2 = _slicedToArray(_unwrapNullable, 2), | ||
typeAnnotation = _unwrapNullable2[0], | ||
nullable = _unwrapNullable2[1]; | ||
const isRequired = !nullable && !isOptional; | ||
const wrapFollyOptional = (type ) => { | ||
return isRequired ? type : `folly::Optional<${type}>`; | ||
const wrapOptional = type => { | ||
return isRequired ? type : `std::optional<${type}>`; | ||
}; | ||
@@ -93,21 +94,42 @@ | ||
case 'RootTag': | ||
return wrapFollyOptional('double'); | ||
return wrapOptional('double'); | ||
default: | ||
(typeAnnotation.name ); | ||
typeAnnotation.name; | ||
throw new Error(`Unknown prop type, found: ${typeAnnotation.name}"`); | ||
} | ||
case 'StringTypeAnnotation': | ||
return 'NSString *'; | ||
case 'NumberTypeAnnotation': | ||
return wrapFollyOptional('double'); | ||
return wrapOptional('double'); | ||
case 'FloatTypeAnnotation': | ||
return wrapFollyOptional('double'); | ||
return wrapOptional('double'); | ||
case 'Int32TypeAnnotation': | ||
return wrapFollyOptional('double'); | ||
return wrapOptional('double'); | ||
case 'DoubleTypeAnnotation': | ||
return wrapFollyOptional('double'); | ||
return wrapOptional('double'); | ||
case 'BooleanTypeAnnotation': | ||
return wrapFollyOptional('bool'); | ||
return wrapOptional('bool'); | ||
case 'EnumDeclaration': | ||
switch (typeAnnotation.memberType) { | ||
case 'NumberTypeAnnotation': | ||
return wrapOptional('double'); | ||
case 'StringTypeAnnotation': | ||
return 'NSString *'; | ||
default: | ||
throw new Error(`Couldn't convert enum into ObjC type: ${typeAnnotation.type}"`); | ||
} | ||
case 'GenericObjectTypeAnnotation': | ||
return isRequired ? 'id<NSObject> ' : 'id<NSObject> _Nullable '; | ||
case 'ArrayTypeAnnotation': | ||
@@ -118,37 +140,25 @@ if (typeAnnotation.elementType == null) { | ||
return wrapFollyOptional( | ||
`std::vector<${toObjCType( | ||
hasteModuleName, | ||
typeAnnotation.elementType, | ||
)}>`, | ||
); | ||
return wrapOptional(`std::vector<${toObjCType(hasteModuleName, typeAnnotation.elementType)}>`); | ||
case 'TypeAliasTypeAnnotation': | ||
const structName = capitalize(typeAnnotation.name); | ||
const namespacedStructName = getNamespacedStructName( | ||
hasteModuleName, | ||
structName, | ||
); | ||
return wrapFollyOptional(`${namespacedStructName}::Builder`); | ||
const namespacedStructName = getNamespacedStructName(hasteModuleName, structName); | ||
return wrapOptional(`${namespacedStructName}::Builder`); | ||
default: | ||
(typeAnnotation.type ); | ||
throw new Error( | ||
`Couldn't convert into ObjC type: ${typeAnnotation.type}"`, | ||
); | ||
typeAnnotation.type; | ||
throw new Error(`Couldn't convert into ObjC type: ${typeAnnotation.type}"`); | ||
} | ||
} | ||
function toObjCValue( | ||
hasteModuleName , | ||
nullableTypeAnnotation , | ||
value , | ||
depth , | ||
isOptional = false, | ||
) { | ||
const [typeAnnotation, nullable] = unwrapNullable(nullableTypeAnnotation); | ||
function toObjCValue(hasteModuleName, nullableTypeAnnotation, value, depth, isOptional = false) { | ||
const _unwrapNullable3 = unwrapNullable(nullableTypeAnnotation), | ||
_unwrapNullable4 = _slicedToArray(_unwrapNullable3, 2), | ||
typeAnnotation = _unwrapNullable4[0], | ||
nullable = _unwrapNullable4[1]; | ||
const isRequired = !nullable && !isOptional; | ||
function wrapPrimitive(type ) { | ||
return !isRequired | ||
? `${value}.hasValue() ? @((${type})${value}.value()) : nil` | ||
: `@(${value})`; | ||
function wrapPrimitive(type) { | ||
return !isRequired ? `${value}.has_value() ? @((${type})${value}.value()) : nil` : `@(${value})`; | ||
} | ||
@@ -161,24 +171,44 @@ | ||
return wrapPrimitive('double'); | ||
default: | ||
(typeAnnotation.name ); | ||
throw new Error( | ||
`Couldn't convert into ObjC type: ${typeAnnotation.type}"`, | ||
); | ||
typeAnnotation.name; | ||
throw new Error(`Couldn't convert into ObjC type: ${typeAnnotation.type}"`); | ||
} | ||
case 'StringTypeAnnotation': | ||
return value; | ||
case 'NumberTypeAnnotation': | ||
return wrapPrimitive('double'); | ||
case 'FloatTypeAnnotation': | ||
return wrapPrimitive('double'); | ||
case 'Int32TypeAnnotation': | ||
return wrapPrimitive('double'); | ||
case 'DoubleTypeAnnotation': | ||
return wrapPrimitive('double'); | ||
case 'BooleanTypeAnnotation': | ||
return wrapPrimitive('BOOL'); | ||
case 'EnumDeclaration': | ||
switch (typeAnnotation.memberType) { | ||
case 'NumberTypeAnnotation': | ||
return wrapPrimitive('double'); | ||
case 'StringTypeAnnotation': | ||
return value; | ||
default: | ||
throw new Error(`Couldn't convert enum into ObjC value: ${typeAnnotation.type}"`); | ||
} | ||
case 'GenericObjectTypeAnnotation': | ||
return value; | ||
case 'ArrayTypeAnnotation': | ||
const {elementType} = typeAnnotation; | ||
const elementType = typeAnnotation.elementType; | ||
if (elementType == null) { | ||
@@ -190,84 +220,64 @@ return value; | ||
const elementObjCType = toObjCType(hasteModuleName, elementType); | ||
const elementObjCValue = toObjCValue( | ||
hasteModuleName, | ||
elementType, | ||
localVarName, | ||
depth + 1, | ||
); | ||
const elementObjCValue = toObjCValue(hasteModuleName, elementType, localVarName, depth + 1); | ||
const RCTConvertVecToArray = transformer => { | ||
return `RCTConvert${ | ||
!isRequired ? 'Optional' : '' | ||
}VecToArray(${value}, ${transformer})`; | ||
return `RCTConvert${!isRequired ? 'Optional' : ''}VecToArray(${value}, ${transformer})`; | ||
}; | ||
return RCTConvertVecToArray( | ||
`^id(${elementObjCType} ${localVarName}) { return ${elementObjCValue}; }`, | ||
); | ||
return RCTConvertVecToArray(`^id(${elementObjCType} ${localVarName}) { return ${elementObjCValue}; }`); | ||
case 'TypeAliasTypeAnnotation': | ||
return !isRequired | ||
? `${value}.hasValue() ? ${value}.value().buildUnsafeRawValue() : nil` | ||
: `${value}.buildUnsafeRawValue()`; | ||
return !isRequired ? `${value}.has_value() ? ${value}.value().buildUnsafeRawValue() : nil` : `${value}.buildUnsafeRawValue()`; | ||
default: | ||
(typeAnnotation.type ); | ||
throw new Error( | ||
`Couldn't convert into ObjC value: ${typeAnnotation.type}"`, | ||
); | ||
typeAnnotation.type; | ||
throw new Error(`Couldn't convert into ObjC value: ${typeAnnotation.type}"`); | ||
} | ||
} | ||
function serializeConstantsStruct( | ||
hasteModuleName , | ||
struct , | ||
) { | ||
function serializeConstantsStruct(hasteModuleName, struct) { | ||
const declaration = StructTemplate({ | ||
hasteModuleName, | ||
structName: struct.name, | ||
builderInputProps: struct.properties | ||
.map(property => { | ||
const {typeAnnotation, optional} = property; | ||
const safePropName = getSafePropertyName(property); | ||
const objCType = toObjCType(hasteModuleName, typeAnnotation, optional); | ||
builderInputProps: struct.properties.map(property => { | ||
const typeAnnotation = property.typeAnnotation, | ||
optional = property.optional; | ||
const safePropName = getSafePropertyName(property); | ||
const objCType = toObjCType(hasteModuleName, typeAnnotation, optional); | ||
if (!optional) { | ||
return `RCTRequired<${objCType}> ${safePropName};`; | ||
} | ||
if (!optional) { | ||
return `RCTRequired<${objCType}> ${safePropName};`; | ||
} | ||
const space = ' '.repeat(objCType.endsWith('*') ? 0 : 1); | ||
return `${objCType}${space}${safePropName};`; | ||
}) | ||
.join('\n '), | ||
const space = ' '.repeat(objCType.endsWith('*') ? 0 : 1); | ||
return `${objCType}${space}${safePropName};`; | ||
}).join('\n ') | ||
}); | ||
const methods = MethodTemplate({ | ||
hasteModuleName, | ||
structName: struct.name, | ||
properties: struct.properties | ||
.map(property => { | ||
const {typeAnnotation, optional, name: propName} = property; | ||
const safePropName = getSafePropertyName(property); | ||
const objCValue = toObjCValue( | ||
hasteModuleName, | ||
typeAnnotation, | ||
safePropName, | ||
0, | ||
optional, | ||
); | ||
properties: struct.properties.map(property => { | ||
const typeAnnotation = property.typeAnnotation, | ||
optional = property.optional, | ||
propName = property.name; | ||
const safePropName = getSafePropertyName(property); | ||
const objCValue = toObjCValue(hasteModuleName, typeAnnotation, safePropName, 0, optional); | ||
let varDecl = `auto ${safePropName} = i.${safePropName}`; | ||
let varDecl = `auto ${safePropName} = i.${safePropName}`; | ||
if (!optional) { | ||
varDecl += '.get()'; | ||
} | ||
if (!optional) { | ||
varDecl += '.get()'; | ||
} | ||
const assignment = `d[@"${propName}"] = ` + objCValue; | ||
return ` ${varDecl};\n ${assignment};`; | ||
}) | ||
.join('\n'), | ||
const assignment = `d[@"${propName}"] = ` + objCValue; | ||
return ` ${varDecl};\n ${assignment};`; | ||
}).join('\n') | ||
}); | ||
return {declaration, methods}; | ||
return { | ||
declaration, | ||
methods | ||
}; | ||
} | ||
module.exports = { | ||
serializeConstantsStruct, | ||
}; | ||
serializeConstantsStruct | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,23 +10,27 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
const {getSafePropertyName, getNamespacedStructName} = require('../Utils'); | ||
const {capitalize} = require('../../../Utils'); | ||
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); } | ||
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } | ||
const {unwrapNullable} = require('../../../../parsers/flow/modules/utils'); | ||
function _iterableToArrayLimit(arr, i) { if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } | ||
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } | ||
const _require = require('../Utils'), | ||
getSafePropertyName = _require.getSafePropertyName, | ||
getNamespacedStructName = _require.getNamespacedStructName; | ||
const _require2 = require('../../../Utils'), | ||
capitalize = _require2.capitalize; | ||
const _require3 = require('../../../../parsers/parsers-commons'), | ||
unwrapNullable = _require3.unwrapNullable; | ||
const StructTemplate = ({ | ||
hasteModuleName, | ||
structName, | ||
structProperties, | ||
} | ||
) => `namespace JS { | ||
structProperties | ||
}) => `namespace JS { | ||
namespace ${hasteModuleName} { | ||
@@ -53,11 +57,4 @@ struct ${structName} { | ||
propertyName, | ||
safePropertyName, | ||
} | ||
) => `inline ${returnType}JS::${hasteModuleName}::${structName}::${safePropertyName}() const | ||
safePropertyName | ||
}) => `inline ${returnType}JS::${hasteModuleName}::${structName}::${safePropertyName}() const | ||
{ | ||
@@ -68,11 +65,12 @@ id const p = _v[@"${propertyName}"]; | ||
function toObjCType( | ||
hasteModuleName , | ||
nullableTypeAnnotation , | ||
isOptional = false, | ||
) { | ||
const [typeAnnotation, nullable] = unwrapNullable(nullableTypeAnnotation); | ||
function toObjCType(hasteModuleName, nullableTypeAnnotation, isOptional = false) { | ||
const _unwrapNullable = unwrapNullable(nullableTypeAnnotation), | ||
_unwrapNullable2 = _slicedToArray(_unwrapNullable, 2), | ||
typeAnnotation = _unwrapNullable2[0], | ||
nullable = _unwrapNullable2[1]; | ||
const isRequired = !nullable && !isOptional; | ||
const wrapFollyOptional = (type ) => { | ||
return isRequired ? type : `folly::Optional<${type}>`; | ||
const wrapOptional = type => { | ||
return isRequired ? type : `std::optional<${type}>`; | ||
}; | ||
@@ -84,21 +82,42 @@ | ||
case 'RootTag': | ||
return wrapFollyOptional('double'); | ||
return wrapOptional('double'); | ||
default: | ||
(typeAnnotation.name ); | ||
typeAnnotation.name; | ||
throw new Error(`Unknown prop type, found: ${typeAnnotation.name}"`); | ||
} | ||
case 'StringTypeAnnotation': | ||
return 'NSString *'; | ||
case 'NumberTypeAnnotation': | ||
return wrapFollyOptional('double'); | ||
return wrapOptional('double'); | ||
case 'FloatTypeAnnotation': | ||
return wrapFollyOptional('double'); | ||
return wrapOptional('double'); | ||
case 'Int32TypeAnnotation': | ||
return wrapFollyOptional('double'); | ||
return wrapOptional('double'); | ||
case 'DoubleTypeAnnotation': | ||
return wrapFollyOptional('double'); | ||
return wrapOptional('double'); | ||
case 'BooleanTypeAnnotation': | ||
return wrapFollyOptional('bool'); | ||
return wrapOptional('bool'); | ||
case 'EnumDeclaration': | ||
switch (typeAnnotation.memberType) { | ||
case 'NumberTypeAnnotation': | ||
return wrapOptional('double'); | ||
case 'StringTypeAnnotation': | ||
return 'NSString *'; | ||
default: | ||
throw new Error(`Couldn't convert enum into ObjC type: ${typeAnnotation.type}"`); | ||
} | ||
case 'GenericObjectTypeAnnotation': | ||
return isRequired ? 'id<NSObject> ' : 'id<NSObject> _Nullable'; | ||
case 'ArrayTypeAnnotation': | ||
@@ -108,37 +127,27 @@ if (typeAnnotation.elementType == null) { | ||
} | ||
return wrapFollyOptional( | ||
`facebook::react::LazyVector<${toObjCType( | ||
hasteModuleName, | ||
typeAnnotation.elementType, | ||
)}>`, | ||
); | ||
return wrapOptional(`facebook::react::LazyVector<${toObjCType(hasteModuleName, typeAnnotation.elementType)}>`); | ||
case 'TypeAliasTypeAnnotation': | ||
const structName = capitalize(typeAnnotation.name); | ||
const namespacedStructName = getNamespacedStructName( | ||
hasteModuleName, | ||
structName, | ||
); | ||
return wrapFollyOptional(namespacedStructName); | ||
const namespacedStructName = getNamespacedStructName(hasteModuleName, structName); | ||
return wrapOptional(namespacedStructName); | ||
default: | ||
(typeAnnotation.type ); | ||
throw new Error( | ||
`Couldn't convert into ObjC type: ${typeAnnotation.type}"`, | ||
); | ||
typeAnnotation.type; | ||
throw new Error(`Couldn't convert into ObjC type: ${typeAnnotation.type}"`); | ||
} | ||
} | ||
function toObjCValue( | ||
hasteModuleName , | ||
nullableTypeAnnotation , | ||
value , | ||
depth , | ||
isOptional = false, | ||
) { | ||
const [typeAnnotation, nullable] = unwrapNullable(nullableTypeAnnotation); | ||
function toObjCValue(hasteModuleName, nullableTypeAnnotation, value, depth, isOptional = false) { | ||
const _unwrapNullable3 = unwrapNullable(nullableTypeAnnotation), | ||
_unwrapNullable4 = _slicedToArray(_unwrapNullable3, 2), | ||
typeAnnotation = _unwrapNullable4[0], | ||
nullable = _unwrapNullable4[1]; | ||
const isRequired = !nullable && !isOptional; | ||
const RCTBridgingTo = (type , arg ) => { | ||
const RCTBridgingTo = (type, arg) => { | ||
const args = [value, arg].filter(Boolean).join(', '); | ||
return isRequired | ||
? `RCTBridgingTo${type}(${args})` | ||
: `RCTBridgingToOptional${type}(${args})`; | ||
return isRequired ? `RCTBridgingTo${type}(${args})` : `RCTBridgingToOptional${type}(${args})`; | ||
}; | ||
@@ -151,24 +160,44 @@ | ||
return RCTBridgingTo('Double'); | ||
default: | ||
(typeAnnotation.name ); | ||
throw new Error( | ||
`Couldn't convert into ObjC type: ${typeAnnotation.type}"`, | ||
); | ||
typeAnnotation.name; | ||
throw new Error(`Couldn't convert into ObjC type: ${typeAnnotation.type}"`); | ||
} | ||
case 'StringTypeAnnotation': | ||
return RCTBridgingTo('String'); | ||
case 'NumberTypeAnnotation': | ||
return RCTBridgingTo('Double'); | ||
case 'FloatTypeAnnotation': | ||
return RCTBridgingTo('Double'); | ||
case 'Int32TypeAnnotation': | ||
return RCTBridgingTo('Double'); | ||
case 'DoubleTypeAnnotation': | ||
return RCTBridgingTo('Double'); | ||
case 'BooleanTypeAnnotation': | ||
return RCTBridgingTo('Bool'); | ||
case 'EnumDeclaration': | ||
switch (typeAnnotation.memberType) { | ||
case 'NumberTypeAnnotation': | ||
return RCTBridgingTo('Double'); | ||
case 'StringTypeAnnotation': | ||
return RCTBridgingTo('String'); | ||
default: | ||
throw new Error(`Couldn't convert enum into ObjC value: ${typeAnnotation.type}"`); | ||
} | ||
case 'GenericObjectTypeAnnotation': | ||
return value; | ||
case 'ArrayTypeAnnotation': | ||
const {elementType} = typeAnnotation; | ||
const elementType = typeAnnotation.elementType; | ||
if (elementType == null) { | ||
@@ -180,85 +209,55 @@ return value; | ||
const elementObjCType = toObjCType(hasteModuleName, elementType); | ||
const elementObjCValue = toObjCValue( | ||
hasteModuleName, | ||
elementType, | ||
localVarName, | ||
depth + 1, | ||
); | ||
const elementObjCValue = toObjCValue(hasteModuleName, elementType, localVarName, depth + 1); | ||
return RCTBridgingTo('Vec', `^${elementObjCType}(id ${localVarName}) { return ${elementObjCValue}; }`); | ||
return RCTBridgingTo( | ||
'Vec', | ||
`^${elementObjCType}(id ${localVarName}) { return ${elementObjCValue}; }`, | ||
); | ||
case 'TypeAliasTypeAnnotation': | ||
const structName = capitalize(typeAnnotation.name); | ||
const namespacedStructName = getNamespacedStructName( | ||
hasteModuleName, | ||
structName, | ||
); | ||
const namespacedStructName = getNamespacedStructName(hasteModuleName, structName); | ||
return !isRequired ? `(${value} == nil ? std::nullopt : std::make_optional(${namespacedStructName}(${value})))` : `${namespacedStructName}(${value})`; | ||
return !isRequired | ||
? `(${value} == nil ? folly::none : folly::make_optional(${namespacedStructName}(${value})))` | ||
: `${namespacedStructName}(${value})`; | ||
default: | ||
(typeAnnotation.type ); | ||
throw new Error( | ||
`Couldn't convert into ObjC value: ${typeAnnotation.type}"`, | ||
); | ||
typeAnnotation.type; | ||
throw new Error(`Couldn't convert into ObjC value: ${typeAnnotation.type}"`); | ||
} | ||
} | ||
function serializeRegularStruct( | ||
hasteModuleName , | ||
struct , | ||
) { | ||
function serializeRegularStruct(hasteModuleName, struct) { | ||
const declaration = StructTemplate({ | ||
hasteModuleName: hasteModuleName, | ||
structName: struct.name, | ||
structProperties: struct.properties | ||
.map(property => { | ||
const {typeAnnotation, optional} = property; | ||
const safePropName = getSafePropertyName(property); | ||
const returnType = toObjCType( | ||
hasteModuleName, | ||
typeAnnotation, | ||
optional, | ||
); | ||
const padding = ' '.repeat(returnType.endsWith('*') ? 0 : 1); | ||
return `${returnType}${padding}${safePropName}() const;`; | ||
}) | ||
.join('\n '), | ||
}); | ||
// $FlowFixMe[missing-type-arg] | ||
const methods = struct.properties | ||
.map (property => { | ||
const {typeAnnotation, optional, name: propName} = property; | ||
const safePropertyName = getSafePropertyName(property); | ||
structProperties: struct.properties.map(property => { | ||
const typeAnnotation = property.typeAnnotation, | ||
optional = property.optional; | ||
const safePropName = getSafePropertyName(property); | ||
const returnType = toObjCType(hasteModuleName, typeAnnotation, optional); | ||
const returnValue = toObjCValue( | ||
hasteModuleName, | ||
typeAnnotation, | ||
'p', | ||
0, | ||
optional, | ||
); | ||
const padding = ' '.repeat(returnType.endsWith('*') ? 0 : 1); | ||
return MethodTemplate({ | ||
hasteModuleName, | ||
structName: struct.name, | ||
returnType: returnType + padding, | ||
returnValue: returnValue, | ||
propertyName: propName, | ||
safePropertyName, | ||
}); | ||
}) | ||
.join('\n'); | ||
return `${returnType}${padding}${safePropName}() const;`; | ||
}).join('\n ') | ||
}); // $FlowFixMe[missing-type-arg] | ||
return {methods, declaration}; | ||
const methods = struct.properties.map(property => { | ||
const typeAnnotation = property.typeAnnotation, | ||
optional = property.optional, | ||
propName = property.name; | ||
const safePropertyName = getSafePropertyName(property); | ||
const returnType = toObjCType(hasteModuleName, typeAnnotation, optional); | ||
const returnValue = toObjCValue(hasteModuleName, typeAnnotation, 'p', 0, optional); | ||
const padding = ' '.repeat(returnType.endsWith('*') ? 0 : 1); | ||
return MethodTemplate({ | ||
hasteModuleName, | ||
structName: struct.name, | ||
returnType: returnType + padding, | ||
returnValue: returnValue, | ||
propertyName: propName, | ||
safePropertyName | ||
}); | ||
}).join('\n'); | ||
return { | ||
methods, | ||
declaration | ||
}; | ||
} | ||
module.exports = { | ||
serializeRegularStruct, | ||
}; | ||
serializeRegularStruct | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,22 +10,15 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
const _require = require('./serializeConstantsStruct'), | ||
serializeConstantsStruct = _require.serializeConstantsStruct; | ||
const {serializeConstantsStruct} = require('./serializeConstantsStruct'); | ||
const {serializeRegularStruct} = require('./serializeRegularStruct'); | ||
const _require2 = require('./serializeRegularStruct'), | ||
serializeRegularStruct = _require2.serializeRegularStruct; | ||
function serializeStruct( | ||
hasteModuleName , | ||
struct , | ||
) { | ||
function serializeStruct(hasteModuleName, struct) { | ||
if (struct.context === 'REGULAR') { | ||
return serializeRegularStruct(hasteModuleName, struct); | ||
} | ||
return serializeConstantsStruct(hasteModuleName, struct); | ||
@@ -35,3 +28,3 @@ } | ||
module.exports = { | ||
serializeStruct, | ||
}; | ||
serializeStruct | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,26 +10,25 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
const _require = require('../Utils'), | ||
createAliasResolver = _require.createAliasResolver, | ||
getModules = _require.getModules; | ||
const {createAliasResolver, getModules} = require('../Utils'); | ||
const _require2 = require('./StructCollector'), | ||
StructCollector = _require2.StructCollector; | ||
const {StructCollector} = require('./StructCollector'); | ||
const {serializeStruct} = require('./header/serializeStruct'); | ||
const {serializeMethod} = require('./serializeMethod'); | ||
const {serializeModuleSource} = require('./source/serializeModule'); | ||
const _require3 = require('./header/serializeStruct'), | ||
serializeStruct = _require3.serializeStruct; | ||
const _require4 = require('./serializeMethod'), | ||
serializeMethod = _require4.serializeMethod; | ||
const _require5 = require('./source/serializeModule'), | ||
serializeModuleSource = _require5.serializeModuleSource; | ||
const ModuleDeclarationTemplate = ({ | ||
hasteModuleName, | ||
structDeclarations, | ||
protocolMethods, | ||
} | ||
) => `${structDeclarations} | ||
protocolMethods | ||
}) => `${structDeclarations} | ||
@protocol ${hasteModuleName}Spec <RCTBridgeModule, RCTTurboModule> | ||
@@ -55,13 +54,8 @@ | ||
structInlineMethods, | ||
assumeNonnull, | ||
} | ||
) => | ||
`/** | ||
* ${'C'}opyright (c) Facebook, Inc. and its affiliates. | ||
assumeNonnull | ||
}) => `/** | ||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* Do not edit this file as changes may cause incorrect behavior and will be lost | ||
* once the code is regenerated. | ||
* | ||
@@ -86,23 +80,15 @@ * ${'@'}generated by codegen project: GenerateModuleObjCpp | ||
#import <ReactCommon/RCTTurboModule.h> | ||
#import <folly/Optional.h> | ||
#import <optional> | ||
#import <vector> | ||
` + | ||
(assumeNonnull ? '\nNS_ASSUME_NONNULL_BEGIN\n' : '') + | ||
moduleDeclarations + | ||
'\n' + | ||
structInlineMethods + | ||
(assumeNonnull ? '\nNS_ASSUME_NONNULL_END\n' : '\n'); | ||
` + (assumeNonnull ? '\nNS_ASSUME_NONNULL_BEGIN\n' : '') + moduleDeclarations + '\n' + structInlineMethods + (assumeNonnull ? '\nNS_ASSUME_NONNULL_END\n' : '\n'); | ||
const SourceFileTemplate = ({ | ||
headerFileName, | ||
moduleImplementations, | ||
} | ||
) => `/** | ||
* ${'C'}opyright (c) Facebook, Inc. and its affiliates. | ||
moduleImplementations | ||
}) => `/** | ||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* Do not edit this file as changes may cause incorrect behavior and will be lost | ||
* once the code is regenerated. | ||
* | ||
@@ -122,39 +108,26 @@ * ${'@'}generated by codegen project: GenerateModuleObjCpp | ||
module.exports = { | ||
generate( | ||
libraryName , | ||
schema , | ||
packageName , | ||
assumeNonnull , | ||
) { | ||
generate(libraryName, schema, packageName, assumeNonnull) { | ||
const nativeModules = getModules(schema); | ||
const moduleDeclarations = []; | ||
const structInlineMethods = []; | ||
const moduleImplementations = []; | ||
const hasteModuleNames = Object.keys(nativeModules).sort(); | ||
const moduleDeclarations = []; | ||
const structInlineMethods = []; | ||
const moduleImplementations = []; | ||
for (const hasteModuleName of hasteModuleNames) { | ||
const _nativeModules$hasteM = nativeModules[hasteModuleName], | ||
aliases = _nativeModules$hasteM.aliases, | ||
excludedPlatforms = _nativeModules$hasteM.excludedPlatforms, | ||
properties = _nativeModules$hasteM.spec.properties; | ||
const hasteModuleNames = Object.keys(nativeModules).sort(); | ||
for (const hasteModuleName of hasteModuleNames) { | ||
const { | ||
aliases, | ||
excludedPlatforms, | ||
spec: {properties}, | ||
} = nativeModules[hasteModuleName]; | ||
if (excludedPlatforms != null && excludedPlatforms.includes('iOS')) { | ||
continue; | ||
} | ||
const resolveAlias = createAliasResolver(aliases); | ||
const structCollector = new StructCollector(); | ||
const methodSerializations = []; | ||
const methodSerializations = []; | ||
const serializeProperty = property => { | ||
methodSerializations.push( | ||
...serializeMethod( | ||
hasteModuleName, | ||
property, | ||
structCollector, | ||
resolveAlias, | ||
), | ||
); | ||
methodSerializations.push(...serializeMethod(hasteModuleName, property, structCollector, resolveAlias)); | ||
}; | ||
/** | ||
@@ -164,9 +137,6 @@ * Note: As we serialize NativeModule methods, we insert structs into | ||
*/ | ||
properties | ||
.filter(property => property.name !== 'getConstants') | ||
.forEach(serializeProperty); | ||
properties | ||
.filter(property => property.name === 'getConstants') | ||
.forEach(serializeProperty); | ||
properties.filter(property => property.name !== 'getConstants').forEach(serializeProperty); | ||
properties.filter(property => property.name === 'getConstants').forEach(serializeProperty); | ||
const generatedStructs = structCollector.getAllStructs(); | ||
@@ -177,3 +147,6 @@ const structStrs = []; | ||
for (const struct of generatedStructs) { | ||
const {methods, declaration} = serializeStruct(hasteModuleName, struct); | ||
const _serializeStruct = serializeStruct(hasteModuleName, struct), | ||
methods = _serializeStruct.methods, | ||
declaration = _serializeStruct.declaration; | ||
structStrs.push(declaration); | ||
@@ -183,23 +156,13 @@ methodStrs.push(methods); | ||
moduleDeclarations.push( | ||
ModuleDeclarationTemplate({ | ||
hasteModuleName: hasteModuleName, | ||
structDeclarations: structStrs.join('\n'), | ||
protocolMethods: methodSerializations | ||
.map(({protocolMethod}) => protocolMethod) | ||
.join('\n'), | ||
}), | ||
); | ||
moduleDeclarations.push(ModuleDeclarationTemplate({ | ||
hasteModuleName: hasteModuleName, | ||
structDeclarations: structStrs.join('\n'), | ||
protocolMethods: methodSerializations.map(({ | ||
protocolMethod | ||
}) => protocolMethod).join('\n') | ||
})); | ||
structInlineMethods.push(methodStrs.join('\n')); | ||
moduleImplementations.push( | ||
serializeModuleSource( | ||
hasteModuleName, | ||
generatedStructs, | ||
methodSerializations.filter( | ||
({selector}) => selector !== '@selector(constantsToExport)', | ||
), | ||
), | ||
); | ||
moduleImplementations.push(serializeModuleSource(hasteModuleName, generatedStructs, methodSerializations.filter(({ | ||
selector | ||
}) => selector !== '@selector(constantsToExport)'))); | ||
} | ||
@@ -211,16 +174,12 @@ | ||
structInlineMethods: structInlineMethods.join('\n'), | ||
assumeNonnull, | ||
assumeNonnull | ||
}); | ||
const sourceFileName = `${libraryName}-generated.mm`; | ||
const sourceFile = SourceFileTemplate({ | ||
headerFileName, | ||
moduleImplementations: moduleImplementations.join('\n'), | ||
moduleImplementations: moduleImplementations.join('\n') | ||
}); | ||
return new Map([[headerFileName, headerFile], [sourceFileName, sourceFile]]); | ||
} | ||
return new Map([ | ||
[headerFileName, headerFile], | ||
[sourceFileName, sourceFile], | ||
]); | ||
}, | ||
}; | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,136 +10,100 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); } | ||
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } | ||
function _iterableToArrayLimit(arr, i) { if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } | ||
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } | ||
const invariant = require('invariant'); | ||
const {StructCollector} = require('./StructCollector'); | ||
const {getNamespacedStructName} = require('./Utils'); | ||
const {capitalize} = require('../../Utils'); | ||
const { | ||
wrapNullable, | ||
unwrapNullable, | ||
} = require('../../../parsers/flow/modules/utils'); | ||
const _require = require('./Utils'), | ||
getNamespacedStructName = _require.getNamespacedStructName; | ||
const _require2 = require('../../Utils'), | ||
capitalize = _require2.capitalize; | ||
const _require3 = require('../../../parsers/parsers-commons'), | ||
wrapNullable = _require3.wrapNullable, | ||
unwrapNullable = _require3.unwrapNullable; | ||
const ProtocolMethodTemplate = ({ | ||
returnObjCType, | ||
methodName, | ||
params, | ||
} | ||
) => `- (${returnObjCType})${methodName}${params};`; | ||
params | ||
}) => `- (${returnObjCType})${methodName}${params};`; | ||
function serializeMethod(hasteModuleName, property, structCollector, resolveAlias) { | ||
const methodName = property.name, | ||
nullableTypeAnnotation = property.typeAnnotation; | ||
const _unwrapNullable = unwrapNullable(nullableTypeAnnotation), | ||
_unwrapNullable2 = _slicedToArray(_unwrapNullable, 1), | ||
propertyTypeAnnotation = _unwrapNullable2[0]; | ||
const params = propertyTypeAnnotation.params; | ||
function serializeMethod( | ||
hasteModuleName , | ||
property , | ||
structCollector , | ||
resolveAlias , | ||
) { | ||
const {name: methodName, typeAnnotation: nullableTypeAnnotation} = property; | ||
const [propertyTypeAnnotation] = unwrapNullable(nullableTypeAnnotation); | ||
const {params} = propertyTypeAnnotation; | ||
if (methodName === 'getConstants') { | ||
return serializeConstantsProtocolMethods( | ||
hasteModuleName, | ||
property, | ||
structCollector, | ||
resolveAlias, | ||
); | ||
return serializeConstantsProtocolMethods(hasteModuleName, property, structCollector, resolveAlias); | ||
} | ||
const methodParams = []; | ||
const structParamRecords = []; | ||
const methodParams = []; | ||
const structParamRecords = []; | ||
params.forEach((param, index) => { | ||
const structName = getParamStructName(methodName, param); | ||
const {objCType, isStruct} = getParamObjCType( | ||
hasteModuleName, | ||
methodName, | ||
param, | ||
structName, | ||
structCollector, | ||
resolveAlias, | ||
); | ||
methodParams.push({paramName: param.name, objCType}); | ||
const _getParamObjCType = getParamObjCType(hasteModuleName, methodName, param, structName, structCollector, resolveAlias), | ||
objCType = _getParamObjCType.objCType, | ||
isStruct = _getParamObjCType.isStruct; | ||
methodParams.push({ | ||
paramName: param.name, | ||
objCType | ||
}); | ||
if (isStruct) { | ||
structParamRecords.push({paramIndex: index, structName}); | ||
structParamRecords.push({ | ||
paramIndex: index, | ||
structName | ||
}); | ||
} | ||
}); | ||
// Unwrap returnTypeAnnotation, so we check if the return type is Promise | ||
}); // Unwrap returnTypeAnnotation, so we check if the return type is Promise | ||
// TODO(T76719514): Disallow nullable PromiseTypeAnnotations | ||
const [returnTypeAnnotation] = unwrapNullable( | ||
propertyTypeAnnotation.returnTypeAnnotation, | ||
); | ||
const _unwrapNullable3 = unwrapNullable(propertyTypeAnnotation.returnTypeAnnotation), | ||
_unwrapNullable4 = _slicedToArray(_unwrapNullable3, 1), | ||
returnTypeAnnotation = _unwrapNullable4[0]; | ||
if (returnTypeAnnotation.type === 'PromiseTypeAnnotation') { | ||
methodParams.push( | ||
{paramName: 'resolve', objCType: 'RCTPromiseResolveBlock'}, | ||
{paramName: 'reject', objCType: 'RCTPromiseRejectBlock'}, | ||
); | ||
methodParams.push({ | ||
paramName: 'resolve', | ||
objCType: 'RCTPromiseResolveBlock' | ||
}, { | ||
paramName: 'reject', | ||
objCType: 'RCTPromiseRejectBlock' | ||
}); | ||
} | ||
/** | ||
* Build Protocol Method | ||
**/ | ||
const returnObjCType = getReturnObjCType( | ||
methodName, | ||
propertyTypeAnnotation.returnTypeAnnotation, | ||
); | ||
const paddingMax = `- (${returnObjCType})${methodName}`.length; | ||
const objCParams = methodParams.reduce( | ||
($objCParams, {objCType, paramName}, i) => { | ||
const rhs = `(${objCType})${paramName}`; | ||
const padding = ' '.repeat(Math.max(0, paddingMax - paramName.length)); | ||
return i === 0 | ||
? `:${rhs}` | ||
: `${$objCParams}\n${padding}${paramName}:${rhs}`; | ||
}, | ||
'', | ||
); | ||
const returnObjCType = getReturnObjCType(methodName, propertyTypeAnnotation.returnTypeAnnotation); | ||
const paddingMax = `- (${returnObjCType})${methodName}`.length; | ||
const objCParams = methodParams.reduce(($objCParams, { | ||
objCType, | ||
paramName | ||
}, i) => { | ||
const rhs = `(${objCType})${paramName}`; | ||
const padding = ' '.repeat(Math.max(0, paddingMax - paramName.length)); | ||
return i === 0 ? `:${rhs}` : `${$objCParams}\n${padding}${paramName}:${rhs}`; | ||
}, ''); | ||
const protocolMethod = ProtocolMethodTemplate({ | ||
methodName, | ||
returnObjCType, | ||
params: objCParams, | ||
params: objCParams | ||
}); | ||
/** | ||
@@ -149,29 +113,28 @@ * Build ObjC Selector | ||
// $FlowFixMe[missing-type-arg] | ||
const selector = methodParams | ||
.map (({paramName}) => paramName) | ||
.reduce(($selector, paramName, i) => { | ||
return i === 0 ? `${$selector}:` : `${$selector}${paramName}:`; | ||
}, methodName); | ||
const selector = methodParams.map(({ | ||
paramName | ||
}) => paramName).reduce(($selector, paramName, i) => { | ||
return i === 0 ? `${$selector}:` : `${$selector}${paramName}:`; | ||
}, methodName); | ||
/** | ||
* Build JS Return type | ||
*/ | ||
const returnJSType = getReturnJSType(methodName, returnTypeAnnotation); | ||
return [ | ||
{ | ||
methodName, | ||
protocolMethod, | ||
selector: `@selector(${selector})`, | ||
structParamRecords, | ||
returnJSType, | ||
argCount: params.length, | ||
}, | ||
]; | ||
return [{ | ||
methodName, | ||
protocolMethod, | ||
selector: `@selector(${selector})`, | ||
structParamRecords, | ||
returnJSType, | ||
argCount: params.length | ||
}]; | ||
} | ||
function getParamStructName(methodName, param) { | ||
const _unwrapNullable5 = unwrapNullable(param.typeAnnotation), | ||
_unwrapNullable6 = _slicedToArray(_unwrapNullable5, 1), | ||
typeAnnotation = _unwrapNullable6[0]; | ||
function getParamStructName(methodName , param ) { | ||
const [typeAnnotation] = unwrapNullable(param.typeAnnotation); | ||
if (typeAnnotation.type === 'TypeAliasTypeAnnotation') { | ||
@@ -184,72 +147,65 @@ return typeAnnotation.name; | ||
function getParamObjCType( | ||
hasteModuleName , | ||
methodName , | ||
param , | ||
structName , | ||
structCollector , | ||
resolveAlias , | ||
) { | ||
const {name: paramName, typeAnnotation: nullableTypeAnnotation} = param; | ||
const [typeAnnotation, nullable] = unwrapNullable(nullableTypeAnnotation); | ||
function getParamObjCType(hasteModuleName, methodName, param, structName, structCollector, resolveAlias) { | ||
const paramName = param.name, | ||
nullableTypeAnnotation = param.typeAnnotation; | ||
const _unwrapNullable7 = unwrapNullable(nullableTypeAnnotation), | ||
_unwrapNullable8 = _slicedToArray(_unwrapNullable7, 2), | ||
typeAnnotation = _unwrapNullable8[0], | ||
nullable = _unwrapNullable8[1]; | ||
const notRequired = param.optional || nullable; | ||
function wrapIntoNullableIfNeeded(generatedType ) { | ||
function wrapIntoNullableIfNeeded(generatedType) { | ||
return nullable ? `${generatedType} _Nullable` : generatedType; | ||
} | ||
const isStruct = (objCType ) => ({ | ||
const isStruct = objCType => ({ | ||
isStruct: true, | ||
objCType, | ||
objCType | ||
}); | ||
const notStruct = (objCType ) => ({ | ||
const notStruct = objCType => ({ | ||
isStruct: false, | ||
objCType, | ||
}); | ||
objCType | ||
}); // Handle types that can only be in parameters | ||
// Handle types that can only be in parameters | ||
switch (typeAnnotation.type) { | ||
case 'FunctionTypeAnnotation': { | ||
return notStruct('RCTResponseSenderBlock'); | ||
} | ||
case 'ArrayTypeAnnotation': { | ||
/** | ||
* Array in params always codegen NSArray * | ||
* | ||
* TODO(T73933406): Support codegen for Arrays of structs and primitives | ||
* | ||
* For example: | ||
* Array<number> => NSArray<NSNumber *> | ||
* type Animal = {}; | ||
* Array<Animal> => NSArray<JS::NativeSampleTurboModule::Animal *>, etc. | ||
*/ | ||
return notStruct(wrapIntoNullableIfNeeded('NSArray *')); | ||
} | ||
case 'FunctionTypeAnnotation': | ||
{ | ||
return notStruct('RCTResponseSenderBlock'); | ||
} | ||
case 'ArrayTypeAnnotation': | ||
{ | ||
/** | ||
* Array in params always codegen NSArray * | ||
* | ||
* TODO(T73933406): Support codegen for Arrays of structs and primitives | ||
* | ||
* For example: | ||
* Array<number> => NSArray<NSNumber *> | ||
* type Animal = {}; | ||
* Array<Animal> => NSArray<JS::NativeSampleTurboModule::Animal *>, etc. | ||
*/ | ||
return notStruct(wrapIntoNullableIfNeeded('NSArray *')); | ||
} | ||
} | ||
const [structTypeAnnotation] = unwrapNullable( | ||
structCollector.process( | ||
structName, | ||
'REGULAR', | ||
resolveAlias, | ||
wrapNullable(nullable, typeAnnotation), | ||
), | ||
); | ||
const _unwrapNullable9 = unwrapNullable(structCollector.process(structName, 'REGULAR', resolveAlias, wrapNullable(nullable, typeAnnotation))), | ||
_unwrapNullable10 = _slicedToArray(_unwrapNullable9, 1), | ||
structTypeAnnotation = _unwrapNullable10[0]; | ||
invariant( | ||
structTypeAnnotation.type !== 'ArrayTypeAnnotation', | ||
'ArrayTypeAnnotations should have been processed earlier', | ||
); | ||
invariant(structTypeAnnotation.type !== 'ArrayTypeAnnotation', 'ArrayTypeAnnotations should have been processed earlier'); | ||
switch (structTypeAnnotation.type) { | ||
case 'TypeAliasTypeAnnotation': { | ||
/** | ||
* TODO(T73943261): Support nullable object literals and aliases? | ||
*/ | ||
return isStruct( | ||
getNamespacedStructName(hasteModuleName, structTypeAnnotation.name) + | ||
' &', | ||
); | ||
} | ||
case 'TypeAliasTypeAnnotation': | ||
{ | ||
/** | ||
* TODO(T73943261): Support nullable object literals and aliases? | ||
*/ | ||
return isStruct(getNamespacedStructName(hasteModuleName, structTypeAnnotation.name) + ' &'); | ||
} | ||
case 'ReservedTypeAnnotation': | ||
@@ -259,37 +215,54 @@ switch (structTypeAnnotation.name) { | ||
return notStruct(notRequired ? 'NSNumber *' : 'double'); | ||
default: | ||
(structTypeAnnotation.name ); | ||
throw new Error( | ||
`Unsupported type for param "${paramName}" in ${methodName}. Found: ${structTypeAnnotation.type}`, | ||
); | ||
structTypeAnnotation.name; | ||
throw new Error(`Unsupported type for param "${paramName}" in ${methodName}. Found: ${structTypeAnnotation.type}`); | ||
} | ||
case 'StringTypeAnnotation': | ||
return notStruct(wrapIntoNullableIfNeeded('NSString *')); | ||
case 'NumberTypeAnnotation': | ||
return notStruct(notRequired ? 'NSNumber *' : 'double'); | ||
case 'FloatTypeAnnotation': | ||
return notStruct(notRequired ? 'NSNumber *' : 'double'); | ||
case 'DoubleTypeAnnotation': | ||
return notStruct(notRequired ? 'NSNumber *' : 'double'); | ||
case 'Int32TypeAnnotation': | ||
return notStruct(notRequired ? 'NSNumber *' : 'double'); | ||
case 'BooleanTypeAnnotation': | ||
return notStruct(notRequired ? 'NSNumber *' : 'BOOL'); | ||
case 'EnumDeclaration': | ||
switch (typeAnnotation.memberType) { | ||
case 'NumberTypeAnnotation': | ||
return notStruct(notRequired ? 'NSNumber *' : 'double'); | ||
case 'StringTypeAnnotation': | ||
return notStruct(wrapIntoNullableIfNeeded('NSString *')); | ||
default: | ||
throw new Error(`Unsupported enum type for param "${paramName}" in ${methodName}. Found: ${typeAnnotation.type}`); | ||
} | ||
case 'GenericObjectTypeAnnotation': | ||
return notStruct(wrapIntoNullableIfNeeded('NSDictionary *')); | ||
default: | ||
(structTypeAnnotation.type ); | ||
throw new Error( | ||
`Unsupported type for param "${paramName}" in ${methodName}. Found: ${typeAnnotation.type}`, | ||
); | ||
structTypeAnnotation.type; | ||
throw new Error(`Unsupported type for param "${paramName}" in ${methodName}. Found: ${typeAnnotation.type}`); | ||
} | ||
} | ||
function getReturnObjCType( | ||
methodName , | ||
nullableTypeAnnotation , | ||
) { | ||
const [typeAnnotation, nullable] = unwrapNullable(nullableTypeAnnotation); | ||
function getReturnObjCType(methodName, nullableTypeAnnotation) { | ||
const _unwrapNullable11 = unwrapNullable(nullableTypeAnnotation), | ||
_unwrapNullable12 = _slicedToArray(_unwrapNullable11, 2), | ||
typeAnnotation = _unwrapNullable12[0], | ||
nullable = _unwrapNullable12[1]; | ||
function wrapIntoNullableIfNeeded(generatedType ) { | ||
function wrapIntoNullableIfNeeded(generatedType) { | ||
return nullable ? `${generatedType} _Nullable` : generatedType; | ||
@@ -301,8 +274,12 @@ } | ||
return 'void'; | ||
case 'PromiseTypeAnnotation': | ||
return 'void'; | ||
case 'ObjectTypeAnnotation': | ||
return wrapIntoNullableIfNeeded('NSDictionary *'); | ||
case 'TypeAliasTypeAnnotation': | ||
return wrapIntoNullableIfNeeded('NSDictionary *'); | ||
case 'ArrayTypeAnnotation': | ||
@@ -313,8 +290,4 @@ if (typeAnnotation.elementType == null) { | ||
return wrapIntoNullableIfNeeded( | ||
`NSArray<${getReturnObjCType( | ||
methodName, | ||
typeAnnotation.elementType, | ||
)}> *`, | ||
); | ||
return wrapIntoNullableIfNeeded(`NSArray<${getReturnObjCType(methodName, typeAnnotation.elementType)}> *`); | ||
case 'ReservedTypeAnnotation': | ||
@@ -324,8 +297,8 @@ switch (typeAnnotation.name) { | ||
return wrapIntoNullableIfNeeded('NSNumber *'); | ||
default: | ||
(typeAnnotation.name ); | ||
throw new Error( | ||
`Unsupported return type for ${methodName}. Found: ${typeAnnotation.name}`, | ||
); | ||
typeAnnotation.name; | ||
throw new Error(`Unsupported return type for ${methodName}. Found: ${typeAnnotation.name}`); | ||
} | ||
case 'StringTypeAnnotation': | ||
@@ -335,80 +308,147 @@ // TODO: Can NSString * returns not be _Nullable? | ||
return wrapIntoNullableIfNeeded('NSString *'); | ||
case 'NumberTypeAnnotation': | ||
return wrapIntoNullableIfNeeded('NSNumber *'); | ||
case 'FloatTypeAnnotation': | ||
return wrapIntoNullableIfNeeded('NSNumber *'); | ||
case 'DoubleTypeAnnotation': | ||
return wrapIntoNullableIfNeeded('NSNumber *'); | ||
case 'Int32TypeAnnotation': | ||
return wrapIntoNullableIfNeeded('NSNumber *'); | ||
case 'BooleanTypeAnnotation': | ||
return wrapIntoNullableIfNeeded('NSNumber *'); | ||
case 'EnumDeclaration': | ||
switch (typeAnnotation.memberType) { | ||
case 'NumberTypeAnnotation': | ||
return wrapIntoNullableIfNeeded('NSNumber *'); | ||
case 'StringTypeAnnotation': | ||
return wrapIntoNullableIfNeeded('NSString *'); | ||
default: | ||
throw new Error(`Unsupported enum return type for ${methodName}. Found: ${typeAnnotation.type}`); | ||
} | ||
case 'UnionTypeAnnotation': | ||
switch (typeAnnotation.memberType) { | ||
case 'NumberTypeAnnotation': | ||
return wrapIntoNullableIfNeeded('NSNumber *'); | ||
case 'ObjectTypeAnnotation': | ||
return wrapIntoNullableIfNeeded('NSDictionary *'); | ||
case 'StringTypeAnnotation': | ||
// TODO: Can NSString * returns not be _Nullable? | ||
// In the legacy codegen, we don't surround NSSTring * with _Nullable | ||
return wrapIntoNullableIfNeeded('NSString *'); | ||
default: | ||
throw new Error(`Unsupported union return type for ${methodName}, found: ${typeAnnotation.memberType}"`); | ||
} | ||
case 'GenericObjectTypeAnnotation': | ||
return wrapIntoNullableIfNeeded('NSDictionary *'); | ||
default: | ||
(typeAnnotation.type ); | ||
throw new Error( | ||
`Unsupported return type for ${methodName}. Found: ${typeAnnotation.type}`, | ||
); | ||
typeAnnotation.type; | ||
throw new Error(`Unsupported return type for ${methodName}. Found: ${typeAnnotation.type}`); | ||
} | ||
} | ||
function getReturnJSType( | ||
methodName , | ||
nullableTypeAnnotation , | ||
) { | ||
const [typeAnnotation] = unwrapNullable(nullableTypeAnnotation); | ||
function getReturnJSType(methodName, nullableTypeAnnotation) { | ||
const _unwrapNullable13 = unwrapNullable(nullableTypeAnnotation), | ||
_unwrapNullable14 = _slicedToArray(_unwrapNullable13, 1), | ||
typeAnnotation = _unwrapNullable14[0]; | ||
switch (typeAnnotation.type) { | ||
case 'VoidTypeAnnotation': | ||
return 'VoidKind'; | ||
case 'PromiseTypeAnnotation': | ||
return 'PromiseKind'; | ||
case 'ObjectTypeAnnotation': | ||
return 'ObjectKind'; | ||
case 'TypeAliasTypeAnnotation': | ||
return 'ObjectKind'; | ||
case 'ArrayTypeAnnotation': | ||
return 'ArrayKind'; | ||
case 'ReservedTypeAnnotation': | ||
return 'NumberKind'; | ||
case 'StringTypeAnnotation': | ||
return 'StringKind'; | ||
case 'NumberTypeAnnotation': | ||
return 'NumberKind'; | ||
case 'FloatTypeAnnotation': | ||
return 'NumberKind'; | ||
case 'DoubleTypeAnnotation': | ||
return 'NumberKind'; | ||
case 'Int32TypeAnnotation': | ||
return 'NumberKind'; | ||
case 'BooleanTypeAnnotation': | ||
return 'BooleanKind'; | ||
case 'GenericObjectTypeAnnotation': | ||
return 'ObjectKind'; | ||
case 'EnumDeclaration': | ||
switch (typeAnnotation.memberType) { | ||
case 'NumberTypeAnnotation': | ||
return 'NumberKind'; | ||
case 'StringTypeAnnotation': | ||
return 'StringKind'; | ||
default: | ||
throw new Error(`Unsupported return type for ${methodName}. Found: ${typeAnnotation.type}`); | ||
} | ||
case 'UnionTypeAnnotation': | ||
switch (typeAnnotation.memberType) { | ||
case 'NumberTypeAnnotation': | ||
return 'NumberKind'; | ||
case 'ObjectTypeAnnotation': | ||
return 'ObjectKind'; | ||
case 'StringTypeAnnotation': | ||
return 'StringKind'; | ||
default: | ||
throw new Error(`Unsupported return type for ${methodName}. Found: ${typeAnnotation.type}`); | ||
} | ||
default: | ||
(typeAnnotation.type ); | ||
throw new Error( | ||
`Unsupported return type for ${methodName}. Found: ${typeAnnotation.type}`, | ||
); | ||
typeAnnotation.type; | ||
throw new Error(`Unsupported return type for ${methodName}. Found: ${typeAnnotation.type}`); | ||
} | ||
} | ||
function serializeConstantsProtocolMethods( | ||
hasteModuleName , | ||
property , | ||
structCollector , | ||
resolveAlias , | ||
) { | ||
const [propertyTypeAnnotation] = unwrapNullable(property.typeAnnotation); | ||
function serializeConstantsProtocolMethods(hasteModuleName, property, structCollector, resolveAlias) { | ||
const _unwrapNullable15 = unwrapNullable(property.typeAnnotation), | ||
_unwrapNullable16 = _slicedToArray(_unwrapNullable15, 1), | ||
propertyTypeAnnotation = _unwrapNullable16[0]; | ||
if (propertyTypeAnnotation.params.length !== 0) { | ||
throw new Error( | ||
`${hasteModuleName}.getConstants() may only accept 0 arguments.`, | ||
); | ||
throw new Error(`${hasteModuleName}.getConstants() may only accept 0 arguments.`); | ||
} | ||
const {returnTypeAnnotation} = propertyTypeAnnotation; | ||
const returnTypeAnnotation = propertyTypeAnnotation.returnTypeAnnotation; | ||
if (returnTypeAnnotation.type !== 'ObjectTypeAnnotation') { | ||
throw new Error( | ||
`${hasteModuleName}.getConstants() may only return an object literal: {...}.`, | ||
); | ||
throw new Error(`${hasteModuleName}.getConstants() may only return an object literal: {...}.`); | ||
} | ||
@@ -420,39 +460,25 @@ | ||
const realTypeAnnotation = structCollector.process( | ||
'Constants', | ||
'CONSTANTS', | ||
resolveAlias, | ||
returnTypeAnnotation, | ||
); | ||
const realTypeAnnotation = structCollector.process('Constants', 'CONSTANTS', resolveAlias, returnTypeAnnotation); | ||
invariant(realTypeAnnotation.type === 'TypeAliasTypeAnnotation', "Unable to generate C++ struct from module's getConstants() method return type."); | ||
const returnObjCType = `facebook::react::ModuleConstants<JS::${hasteModuleName}::Constants::Builder>`; // $FlowFixMe[missing-type-arg] | ||
invariant( | ||
realTypeAnnotation.type === 'TypeAliasTypeAnnotation', | ||
"Unable to generate C++ struct from module's getConstants() method return type.", | ||
); | ||
const returnObjCType = `facebook::react::ModuleConstants<JS::${hasteModuleName}::Constants::Builder>`; | ||
// $FlowFixMe[missing-type-arg] | ||
return ['constantsToExport', 'getConstants'].map ( | ||
methodName => { | ||
const protocolMethod = ProtocolMethodTemplate({ | ||
methodName, | ||
returnObjCType, | ||
params: '', | ||
}); | ||
return { | ||
methodName, | ||
protocolMethod, | ||
returnJSType: 'ObjectKind', | ||
selector: `@selector(${methodName})`, | ||
structParamRecords: [], | ||
argCount: 0, | ||
}; | ||
}, | ||
); | ||
return ['constantsToExport', 'getConstants'].map(methodName => { | ||
const protocolMethod = ProtocolMethodTemplate({ | ||
methodName, | ||
returnObjCType, | ||
params: '' | ||
}); | ||
return { | ||
methodName, | ||
protocolMethod, | ||
returnJSType: 'ObjectKind', | ||
selector: `@selector(${methodName})`, | ||
structParamRecords: [], | ||
argCount: 0 | ||
}; | ||
}); | ||
} | ||
module.exports = { | ||
serializeMethod, | ||
}; | ||
serializeMethod | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,49 +10,33 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
const ModuleTemplate = ({ | ||
hasteModuleName, | ||
structs, | ||
methodSerializationOutputs, | ||
} | ||
) => `${structs | ||
.map(struct => | ||
RCTCxxConvertCategoryTemplate({hasteModuleName, structName: struct.name}), | ||
) | ||
.join('\n')} | ||
methodSerializationOutputs | ||
}) => `${structs.map(struct => RCTCxxConvertCategoryTemplate({ | ||
hasteModuleName, | ||
structName: struct.name | ||
})).join('\n')} | ||
namespace facebook { | ||
namespace react { | ||
${methodSerializationOutputs | ||
.map(serializedMethodParts => | ||
InlineHostFunctionTemplate({ | ||
hasteModuleName, | ||
methodName: serializedMethodParts.methodName, | ||
returnJSType: serializedMethodParts.returnJSType, | ||
selector: serializedMethodParts.selector, | ||
}), | ||
) | ||
.join('\n')} | ||
${methodSerializationOutputs.map(serializedMethodParts => InlineHostFunctionTemplate({ | ||
hasteModuleName, | ||
methodName: serializedMethodParts.methodName, | ||
returnJSType: serializedMethodParts.returnJSType, | ||
selector: serializedMethodParts.selector | ||
})).join('\n')} | ||
${hasteModuleName}SpecJSI::${hasteModuleName}SpecJSI(const ObjCTurboModule::InitParams ¶ms) | ||
: ObjCTurboModule(params) { | ||
${methodSerializationOutputs | ||
.map(({methodName, structParamRecords, argCount}) => | ||
MethodMapEntryTemplate({ | ||
hasteModuleName, | ||
methodName, | ||
structParamRecords, | ||
argCount, | ||
}), | ||
) | ||
.join('\n' + ' '.repeat(8))} | ||
${methodSerializationOutputs.map(({ | ||
methodName, | ||
structParamRecords, | ||
argCount | ||
}) => MethodMapEntryTemplate({ | ||
hasteModuleName, | ||
methodName, | ||
structParamRecords, | ||
argCount | ||
})).join('\n' + ' '.repeat(8))} | ||
} | ||
@@ -64,7 +48,4 @@ } // namespace react | ||
hasteModuleName, | ||
structName, | ||
} | ||
) => `@implementation RCTCxxConvert (${hasteModuleName}_${structName}) | ||
structName | ||
}) => `@implementation RCTCxxConvert (${hasteModuleName}_${structName}) | ||
+ (RCTManagedPointer *)JS_${hasteModuleName}_${structName}:(id)json | ||
@@ -80,9 +61,4 @@ { | ||
returnJSType, | ||
selector, | ||
} | ||
) => ` | ||
selector | ||
}) => ` | ||
static facebook::jsi::Value __hostFunction_${hasteModuleName}SpecJSI_${methodName}(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { | ||
@@ -96,25 +72,19 @@ return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, ${returnJSType}, "${methodName}", ${selector}, args, count); | ||
structParamRecords, | ||
argCount, | ||
} | ||
) => ` | ||
argCount | ||
}) => ` | ||
methodMap_["${methodName}"] = MethodMetadata {${argCount}, __hostFunction_${hasteModuleName}SpecJSI_${methodName}}; | ||
${structParamRecords | ||
.map(({paramIndex, structName}) => { | ||
return `setMethodArgConversionSelector(@"${methodName}", ${paramIndex}, @"JS_${hasteModuleName}_${structName}:");`; | ||
}) | ||
.join('\n' + ' '.repeat(8))}`; | ||
${structParamRecords.map(({ | ||
paramIndex, | ||
structName | ||
}) => { | ||
return `setMethodArgConversionSelector(@"${methodName}", ${paramIndex}, @"JS_${hasteModuleName}_${structName}:");`; | ||
}).join('\n' + ' '.repeat(8))}`; | ||
function serializeModuleSource( | ||
hasteModuleName , | ||
structs , | ||
methodSerializationOutputs , | ||
) { | ||
function serializeModuleSource(hasteModuleName, structs, methodSerializationOutputs) { | ||
return ModuleTemplate({ | ||
hasteModuleName, | ||
structs: structs.filter(({context}) => context !== 'CONSTANTS'), | ||
methodSerializationOutputs, | ||
structs: structs.filter(({ | ||
context | ||
}) => context !== 'CONSTANTS'), | ||
methodSerializationOutputs | ||
}); | ||
@@ -124,3 +94,3 @@ } | ||
module.exports = { | ||
serializeModuleSource, | ||
}; | ||
serializeModuleSource | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,158 +10,101 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (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 = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } | ||
const {capitalize} = require('../../Utils'); | ||
const { | ||
unwrapNullable, | ||
wrapNullable, | ||
} = require('../../../parsers/flow/modules/utils'); | ||
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); } | ||
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } | ||
function _iterableToArrayLimit(arr, i) { if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } | ||
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
const _require = require('../../Utils'), | ||
capitalize = _require.capitalize; | ||
const _require2 = require('../../../parsers/parsers-commons'), | ||
unwrapNullable = _require2.unwrapNullable, | ||
wrapNullable = _require2.wrapNullable; | ||
class StructCollector { | ||
_structs = new Map(); | ||
constructor() { | ||
_defineProperty(this, "_structs", new Map()); | ||
} | ||
process( | ||
structName , | ||
structContext , | ||
resolveAlias , | ||
nullableTypeAnnotation , | ||
) { | ||
const [typeAnnotation, nullable] = unwrapNullable(nullableTypeAnnotation); | ||
process(structName, structContext, resolveAlias, nullableTypeAnnotation) { | ||
const _unwrapNullable = unwrapNullable(nullableTypeAnnotation), | ||
_unwrapNullable2 = _slicedToArray(_unwrapNullable, 2), | ||
typeAnnotation = _unwrapNullable2[0], | ||
nullable = _unwrapNullable2[1]; | ||
switch (typeAnnotation.type) { | ||
case 'ObjectTypeAnnotation': { | ||
this._insertStruct( | ||
structName, | ||
structContext, | ||
resolveAlias, | ||
typeAnnotation, | ||
); | ||
return wrapNullable(nullable, { | ||
type: 'TypeAliasTypeAnnotation', | ||
name: structName, | ||
}); | ||
} | ||
case 'ArrayTypeAnnotation': { | ||
if (typeAnnotation.elementType == null) { | ||
case 'ObjectTypeAnnotation': | ||
{ | ||
this._insertStruct(structName, structContext, resolveAlias, typeAnnotation); | ||
return wrapNullable(nullable, { | ||
type: 'TypeAliasTypeAnnotation', | ||
name: structName | ||
}); | ||
} | ||
case 'ArrayTypeAnnotation': | ||
{ | ||
if (typeAnnotation.elementType == null) { | ||
return wrapNullable(nullable, { | ||
type: 'ArrayTypeAnnotation' | ||
}); | ||
} | ||
return wrapNullable(nullable, { | ||
type: 'ArrayTypeAnnotation', | ||
elementType: this.process(structName + 'Element', structContext, resolveAlias, typeAnnotation.elementType) | ||
}); | ||
} | ||
return wrapNullable(nullable, { | ||
type: 'ArrayTypeAnnotation', | ||
elementType: this.process( | ||
structName + 'Element', | ||
structContext, | ||
resolveAlias, | ||
typeAnnotation.elementType, | ||
), | ||
}); | ||
} | ||
case 'TypeAliasTypeAnnotation': { | ||
this._insertAlias(typeAnnotation.name, structContext, resolveAlias); | ||
case 'TypeAliasTypeAnnotation': | ||
{ | ||
this._insertAlias(typeAnnotation.name, structContext, resolveAlias); | ||
return wrapNullable(nullable, typeAnnotation); | ||
} | ||
case 'EnumDeclaration': | ||
return wrapNullable(nullable, typeAnnotation); | ||
} | ||
default: { | ||
return wrapNullable(nullable, typeAnnotation); | ||
} | ||
case 'MixedTypeAnnotation': | ||
throw new Error('Mixed types are unsupported in structs'); | ||
case 'UnionTypeAnnotation': | ||
throw new Error('Union types are unsupported in structs'); | ||
default: | ||
{ | ||
return wrapNullable(nullable, typeAnnotation); | ||
} | ||
} | ||
} | ||
_insertAlias( | ||
aliasName , | ||
structContext , | ||
resolveAlias , | ||
) { | ||
_insertAlias(aliasName, structContext, resolveAlias) { | ||
const usedStruct = this._structs.get(aliasName); | ||
if (usedStruct == null) { | ||
this._insertStruct( | ||
aliasName, | ||
structContext, | ||
resolveAlias, | ||
resolveAlias(aliasName), | ||
); | ||
this._insertStruct(aliasName, structContext, resolveAlias, resolveAlias(aliasName)); | ||
} else if (usedStruct.context !== structContext) { | ||
throw new Error( | ||
`Tried to use alias '${aliasName}' in a getConstants() return type and inside a regular struct.`, | ||
); | ||
throw new Error(`Tried to use alias '${aliasName}' in a getConstants() return type and inside a regular struct.`); | ||
} | ||
} | ||
_insertStruct( | ||
structName , | ||
structContext , | ||
resolveAlias , | ||
objectTypeAnnotation , | ||
) { | ||
_insertStruct(structName, structContext, resolveAlias, objectTypeAnnotation) { | ||
// $FlowFixMe[missing-type-arg] | ||
const properties = objectTypeAnnotation.properties.map | ||
(property => { | ||
const properties = objectTypeAnnotation.properties.map(property => { | ||
const propertyStructName = structName + capitalize(property.name); | ||
return { | ||
...property, | ||
typeAnnotation: this.process( | ||
propertyStructName, | ||
structContext, | ||
resolveAlias, | ||
property.typeAnnotation, | ||
), | ||
}; | ||
return _objectSpread({}, property, { | ||
typeAnnotation: this.process(propertyStructName, structContext, resolveAlias, property.typeAnnotation) | ||
}); | ||
}); | ||
@@ -174,5 +117,7 @@ | ||
context: 'REGULAR', | ||
properties: properties, | ||
properties: properties | ||
}); | ||
break; | ||
case 'CONSTANTS': | ||
@@ -182,7 +127,9 @@ this._structs.set(structName, { | ||
context: 'CONSTANTS', | ||
properties: properties, | ||
properties: properties | ||
}); | ||
break; | ||
default: | ||
(structContext ); | ||
structContext; | ||
throw new Error(`Detected an invalid struct context: ${structContext}`); | ||
@@ -192,13 +139,14 @@ } | ||
getAllStructs() { | ||
getAllStructs() { | ||
return [...this._structs.values()]; | ||
} | ||
getStruct(name ) { | ||
getStruct(name) { | ||
return this._structs.get(name); | ||
} | ||
} | ||
module.exports = { | ||
StructCollector, | ||
}; | ||
StructCollector | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,18 +10,13 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
function getSafePropertyName(property ) { | ||
function getSafePropertyName(property) { | ||
if (property.name === 'id') { | ||
return `${property.name}_`; | ||
} | ||
return property.name; | ||
} | ||
function getNamespacedStructName( | ||
hasteModuleName , | ||
structName , | ||
) { | ||
function getNamespacedStructName(hasteModuleName, structName) { | ||
return `JS::${hasteModuleName}::${structName}`; | ||
@@ -32,3 +27,3 @@ } | ||
getSafePropertyName, | ||
getNamespacedStructName, | ||
}; | ||
getNamespacedStructName | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,20 +10,8 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
const invariant = require('invariant'); | ||
function createAliasResolver(aliasMap ) { | ||
return (aliasName ) => { | ||
function createAliasResolver(aliasMap) { | ||
return aliasName => { | ||
const alias = aliasMap[aliasName]; | ||
@@ -35,16 +23,13 @@ invariant(alias != null, `Unable to resolve type alias '${aliasName}'.`); | ||
function getModules( | ||
schema , | ||
) { | ||
return Object.keys(schema.modules).reduce ( | ||
(modules, hasteModuleName ) => { | ||
const module = schema.modules[hasteModuleName]; | ||
if (module == null || module.type === 'Component') { | ||
return modules; | ||
} | ||
modules[hasteModuleName] = module; | ||
function getModules(schema) { | ||
return Object.keys(schema.modules).reduce((modules, hasteModuleName) => { | ||
const module = schema.modules[hasteModuleName]; | ||
if (module == null || module.type === 'Component') { | ||
return modules; | ||
}, | ||
{}, | ||
); | ||
} | ||
modules[hasteModuleName] = module; | ||
return modules; | ||
}, {}); | ||
} | ||
@@ -54,3 +39,3 @@ | ||
createAliasResolver, | ||
getModules, | ||
}; | ||
getModules | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,5 +10,3 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
/* | ||
@@ -21,168 +19,196 @@ TODO: | ||
const fs = require('fs'); | ||
const generateComponentDescriptorH = require('./components/GenerateComponentDescriptorH.js'); | ||
const generateComponentHObjCpp = require('./components/GenerateComponentHObjCpp.js'); | ||
const generateEventEmitterCpp = require('./components/GenerateEventEmitterCpp.js'); | ||
const generateEventEmitterH = require('./components/GenerateEventEmitterH.js'); | ||
const generatePropsCpp = require('./components/GeneratePropsCpp.js'); | ||
const generatePropsH = require('./components/GeneratePropsH.js'); | ||
const generateStateCpp = require('./components/GenerateStateCpp.js'); | ||
const generateStateH = require('./components/GenerateStateH.js'); | ||
const generateModuleH = require('./modules/GenerateModuleH.js'); | ||
const generateModuleCpp = require('./modules/GenerateModuleCpp.js'); | ||
const generateModuleObjCpp = require('./modules/GenerateModuleObjCpp'); | ||
const generateModuleJavaSpec = require('./modules/GenerateModuleJavaSpec.js'); | ||
const GenerateModuleJniCpp = require('./modules/GenerateModuleJniCpp.js'); | ||
const GenerateModuleJniH = require('./modules/GenerateModuleJniH.js'); | ||
const generatePropsJavaInterface = require('./components/GeneratePropsJavaInterface.js'); | ||
const generatePropsJavaDelegate = require('./components/GeneratePropsJavaDelegate.js'); | ||
const generateTests = require('./components/GenerateTests.js'); | ||
const generateShadowNodeCpp = require('./components/GenerateShadowNodeCpp.js'); | ||
const generateShadowNodeH = require('./components/GenerateShadowNodeH.js'); | ||
const generateViewConfigJs = require('./components/GenerateViewConfigJs.js'); | ||
const path = require('path'); | ||
const schemaValidator = require('../SchemaValidator.js'); | ||
const generateThirdPartyFabricComponentsProviderObjCpp = require('./components/GenerateThirdPartyFabricComponentsProviderObjCpp.js'); | ||
const generateThirdPartyFabricComponentsProviderH = require('./components/GenerateThirdPartyFabricComponentsProviderH.js'); | ||
const generateViewConfigJs = require('./components/GenerateViewConfigJs.js'); | ||
const path = require('path'); | ||
const GENERATORS = { | ||
const schemaValidator = require('../SchemaValidator.js'); | ||
const LIBRARY_GENERATORS = { | ||
descriptors: [generateComponentDescriptorH.generate], | ||
events: [generateEventEmitterCpp.generate, generateEventEmitterH.generate], | ||
props: [ | ||
generateComponentHObjCpp.generate, | ||
generatePropsCpp.generate, | ||
generatePropsH.generate, | ||
generatePropsJavaInterface.generate, | ||
generatePropsJavaDelegate.generate, | ||
], | ||
states: [generateStateCpp.generate, generateStateH.generate], | ||
props: [generateComponentHObjCpp.generate, generatePropsCpp.generate, generatePropsH.generate, generatePropsJavaInterface.generate, generatePropsJavaDelegate.generate], | ||
// TODO: Refactor this to consolidate various C++ output variation instead of forking per platform. | ||
componentsAndroid: [ | ||
// JNI/C++ files | ||
generateComponentDescriptorH.generate, | ||
generateEventEmitterCpp.generate, | ||
generateEventEmitterH.generate, | ||
generatePropsCpp.generate, | ||
generatePropsH.generate, | ||
generateShadowNodeCpp.generate, | ||
generateShadowNodeH.generate, | ||
// Java files | ||
generatePropsJavaInterface.generate, | ||
generatePropsJavaDelegate.generate, | ||
], | ||
componentsIOS: [ | ||
generateComponentDescriptorH.generate, | ||
generateEventEmitterCpp.generate, | ||
generateEventEmitterH.generate, | ||
generateComponentHObjCpp.generate, | ||
generatePropsCpp.generate, | ||
generatePropsH.generate, | ||
generateShadowNodeCpp.generate, | ||
generateShadowNodeH.generate, | ||
], | ||
modulesAndroid: [ | ||
GenerateModuleJniCpp.generate, | ||
GenerateModuleJniH.generate, | ||
generateModuleJavaSpec.generate, | ||
], | ||
componentsAndroid: [// JNI/C++ files | ||
generateComponentDescriptorH.generate, generateEventEmitterCpp.generate, generateEventEmitterH.generate, generatePropsCpp.generate, generatePropsH.generate, generateStateCpp.generate, generateStateH.generate, generateShadowNodeCpp.generate, generateShadowNodeH.generate, // Java files | ||
generatePropsJavaInterface.generate, generatePropsJavaDelegate.generate], | ||
componentsIOS: [generateComponentDescriptorH.generate, generateEventEmitterCpp.generate, generateEventEmitterH.generate, generateComponentHObjCpp.generate, generatePropsCpp.generate, generatePropsH.generate, generateStateCpp.generate, generateStateH.generate, generateShadowNodeCpp.generate, generateShadowNodeH.generate], | ||
modulesAndroid: [GenerateModuleJniCpp.generate, GenerateModuleJniH.generate, generateModuleJavaSpec.generate], | ||
modulesCxx: [generateModuleCpp.generate, generateModuleH.generate], | ||
modulesIOS: [generateModuleObjCpp.generate], | ||
tests: [generateTests.generate], | ||
'shadow-nodes': [ | ||
generateShadowNodeCpp.generate, | ||
generateShadowNodeH.generate, | ||
], | ||
'shadow-nodes': [generateShadowNodeCpp.generate, generateShadowNodeH.generate] | ||
}; | ||
const SCHEMAS_GENERATORS = { | ||
providerIOS: [generateThirdPartyFabricComponentsProviderObjCpp.generate, generateThirdPartyFabricComponentsProviderH.generate] | ||
}; | ||
function writeMapToFiles(map , outputDir ) { | ||
function writeMapToFiles(map) { | ||
let success = true; | ||
map.forEach((contents , fileName ) => { | ||
map.forEach(file => { | ||
try { | ||
const location = path.join(outputDir, fileName); | ||
const location = path.join(file.outputDir, file.name); | ||
const dirName = path.dirname(location); | ||
if (!fs.existsSync(dirName)) { | ||
fs.mkdirSync(dirName, {recursive: true}); | ||
fs.mkdirSync(dirName, { | ||
recursive: true | ||
}); | ||
} | ||
fs.writeFileSync(location, contents); | ||
fs.writeFileSync(location, file.content); | ||
} catch (error) { | ||
success = false; | ||
console.error(`Failed to write ${fileName} to ${outputDir}`, error); | ||
console.error(`Failed to write ${file.name} to ${file.outputDir}`, error); | ||
} | ||
}); | ||
return success; | ||
} | ||
function checkFilesForChanges( | ||
map , | ||
outputDir , | ||
) { | ||
function checkFilesForChanges(generated) { | ||
let hasChanged = false; | ||
map.forEach((contents , fileName ) => { | ||
const location = path.join(outputDir, fileName); | ||
generated.forEach(file => { | ||
const location = path.join(file.outputDir, file.name); | ||
const currentContents = fs.readFileSync(location, 'utf8'); | ||
if (currentContents !== contents) { | ||
console.error(`- ${fileName} has changed`); | ||
if (currentContents !== file.content) { | ||
console.error(`- ${file.name} has changed`); | ||
hasChanged = true; | ||
} | ||
}); | ||
return !hasChanged; | ||
} | ||
function checkOrWriteFiles(generatedFiles, test) { | ||
if (test === true) { | ||
return checkFilesForChanges(generatedFiles); | ||
} | ||
return writeMapToFiles(generatedFiles); | ||
} | ||
module.exports = { | ||
generate( | ||
{libraryName, schema, outputDirectory, packageName, assumeNonnull} , | ||
{generators, test} , | ||
) { | ||
generate({ | ||
libraryName, | ||
schema, | ||
outputDirectory, | ||
packageName, | ||
assumeNonnull | ||
}, { | ||
generators, | ||
test | ||
}) { | ||
schemaValidator.validate(schema); | ||
function composePath(intermediate) { | ||
return path.join(outputDirectory, intermediate, libraryName); | ||
} | ||
const componentIOSOutput = composePath('react/renderer/components/'); | ||
const modulesIOSOutput = composePath('./'); | ||
const outputFoldersForGenerators = { | ||
componentsIOS: componentIOSOutput, | ||
modulesIOS: modulesIOSOutput, | ||
descriptors: outputDirectory, | ||
events: outputDirectory, | ||
props: outputDirectory, | ||
states: outputDirectory, | ||
componentsAndroid: outputDirectory, | ||
modulesAndroid: outputDirectory, | ||
modulesCxx: outputDirectory, | ||
tests: outputDirectory, | ||
'shadow-nodes': outputDirectory | ||
}; | ||
const generatedFiles = []; | ||
for (const name of generators) { | ||
for (const generator of GENERATORS[name]) { | ||
generatedFiles.push( | ||
...generator(libraryName, schema, packageName, assumeNonnull), | ||
); | ||
for (const generator of LIBRARY_GENERATORS[name]) { | ||
generator(libraryName, schema, packageName, assumeNonnull).forEach((contents, fileName) => { | ||
generatedFiles.push({ | ||
name: fileName, | ||
content: contents, | ||
outputDir: outputFoldersForGenerators[name] | ||
}); | ||
}); | ||
} | ||
} | ||
const filesToUpdate = new Map([...generatedFiles]); | ||
return checkOrWriteFiles(generatedFiles, test); | ||
}, | ||
if (test === true) { | ||
return checkFilesForChanges(filesToUpdate, outputDirectory); | ||
generateFromSchemas({ | ||
schemas, | ||
outputDirectory | ||
}, { | ||
generators, | ||
test | ||
}) { | ||
Object.keys(schemas).forEach(libraryName => schemaValidator.validate(schemas[libraryName])); | ||
const generatedFiles = []; | ||
for (const name of generators) { | ||
for (const generator of SCHEMAS_GENERATORS[name]) { | ||
generator(schemas).forEach((contents, fileName) => { | ||
generatedFiles.push({ | ||
name: fileName, | ||
content: contents, | ||
outputDir: outputDirectory | ||
}); | ||
}); | ||
} | ||
} | ||
return writeMapToFiles(filesToUpdate, outputDirectory); | ||
return checkOrWriteFiles(generatedFiles, test); | ||
}, | ||
generateViewConfig({libraryName, schema} ) { | ||
generateViewConfig({ | ||
libraryName, | ||
schema | ||
}) { | ||
schemaValidator.validate(schema); | ||
const result = generateViewConfigJs.generate(libraryName, schema).values().next(); | ||
const result = generateViewConfigJs | ||
.generate(libraryName, schema) | ||
.values() | ||
.next(); | ||
if (typeof result.value !== 'string') { | ||
@@ -193,3 +219,4 @@ throw new Error(`Failed to generate view config for ${libraryName}`); | ||
return result.value; | ||
}, | ||
}; | ||
} | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,11 +10,22 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
function capitalize(string ) { | ||
function capitalize(string) { | ||
return string.charAt(0).toUpperCase() + string.slice(1); | ||
} | ||
function indent(nice, spaces) { | ||
return nice.split('\n').map((line, index) => { | ||
if (line.length === 0 || index === 0) { | ||
return line; | ||
} | ||
const emptySpaces = new Array(spaces + 1).join(' '); | ||
return emptySpaces + line; | ||
}).join('\n'); | ||
} | ||
module.exports = { | ||
capitalize, | ||
}; | ||
indent | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,33 +10,15 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
const _require = require('../utils.js'), | ||
getValueFromTypes = _require.getValueFromTypes; | ||
const {getValueFromTypes} = require('../utils.js'); | ||
function buildCommandSchema(property, types ) { | ||
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<>`, | ||
); | ||
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<>`); | ||
} | ||
@@ -47,6 +29,3 @@ | ||
const paramValue = getValueFromTypes(param.typeAnnotation, types); | ||
const type = | ||
paramValue.type === 'GenericTypeAnnotation' | ||
? paramValue.id.name | ||
: paramValue.type; | ||
const type = paramValue.type === 'GenericTypeAnnotation' ? paramValue.id.name : paramValue.type; | ||
let returnType; | ||
@@ -58,35 +37,39 @@ | ||
type: 'ReservedTypeAnnotation', | ||
name: 'RootTag', | ||
name: 'RootTag' | ||
}; | ||
break; | ||
case 'BooleanTypeAnnotation': | ||
returnType = { | ||
type: 'BooleanTypeAnnotation', | ||
type: 'BooleanTypeAnnotation' | ||
}; | ||
break; | ||
case 'Int32': | ||
returnType = { | ||
type: 'Int32TypeAnnotation', | ||
type: 'Int32TypeAnnotation' | ||
}; | ||
break; | ||
case 'Double': | ||
returnType = { | ||
type: 'DoubleTypeAnnotation', | ||
type: 'DoubleTypeAnnotation' | ||
}; | ||
break; | ||
case 'Float': | ||
returnType = { | ||
type: 'FloatTypeAnnotation', | ||
type: 'FloatTypeAnnotation' | ||
}; | ||
break; | ||
case 'StringTypeAnnotation': | ||
returnType = { | ||
type: 'StringTypeAnnotation', | ||
type: 'StringTypeAnnotation' | ||
}; | ||
break; | ||
default: | ||
(type ); | ||
throw new Error( | ||
`Unsupported param type for method "${name}", param "${paramName}". Found ${type}`, | ||
); | ||
type; | ||
throw new Error(`Unsupported param type for method "${name}", param "${paramName}". Found ${type}`); | ||
} | ||
@@ -96,6 +79,6 @@ | ||
name: paramName, | ||
typeAnnotation: returnType, | ||
optional: false, | ||
typeAnnotation: returnType | ||
}; | ||
}); | ||
return { | ||
@@ -108,20 +91,14 @@ name, | ||
returnTypeAnnotation: { | ||
type: 'VoidTypeAnnotation', | ||
}, | ||
}, | ||
type: 'VoidTypeAnnotation' | ||
} | ||
} | ||
}; | ||
} | ||
function getCommands( | ||
commandTypeAST , | ||
types , | ||
) { | ||
return commandTypeAST | ||
.filter(property => property.type === 'ObjectTypeProperty') | ||
.map(property => buildCommandSchema(property, types)) | ||
.filter(Boolean); | ||
function getCommands(commandTypeAST, types) { | ||
return commandTypeAST.filter(property => property.type === 'ObjectTypeProperty').map(property => buildCommandSchema(property, types)).filter(Boolean); | ||
} | ||
module.exports = { | ||
getCommands, | ||
}; | ||
getCommands | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,20 +10,9 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
function getPropertyType( | ||
name, | ||
optional, | ||
typeAnnotation, | ||
) { | ||
const type = | ||
typeAnnotation.type === 'GenericTypeAnnotation' | ||
? typeAnnotation.id.name | ||
: typeAnnotation.type; | ||
/* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's | ||
* LTI update could not be added via codemod */ | ||
name, optional, typeAnnotation) { | ||
const type = typeAnnotation.type === 'GenericTypeAnnotation' ? typeAnnotation.id.name : typeAnnotation.type; | ||
@@ -36,5 +25,6 @@ switch (type) { | ||
typeAnnotation: { | ||
type: 'BooleanTypeAnnotation', | ||
}, | ||
type: 'BooleanTypeAnnotation' | ||
} | ||
}; | ||
case 'StringTypeAnnotation': | ||
@@ -45,5 +35,6 @@ return { | ||
typeAnnotation: { | ||
type: 'StringTypeAnnotation', | ||
}, | ||
type: 'StringTypeAnnotation' | ||
} | ||
}; | ||
case 'Int32': | ||
@@ -54,5 +45,6 @@ return { | ||
typeAnnotation: { | ||
type: 'Int32TypeAnnotation', | ||
}, | ||
type: 'Int32TypeAnnotation' | ||
} | ||
}; | ||
case 'Double': | ||
@@ -63,5 +55,6 @@ return { | ||
typeAnnotation: { | ||
type: 'DoubleTypeAnnotation', | ||
}, | ||
type: 'DoubleTypeAnnotation' | ||
} | ||
}; | ||
case 'Float': | ||
@@ -72,11 +65,9 @@ return { | ||
typeAnnotation: { | ||
type: 'FloatTypeAnnotation', | ||
}, | ||
type: 'FloatTypeAnnotation' | ||
} | ||
}; | ||
case '$ReadOnly': | ||
return getPropertyType( | ||
name, | ||
optional, | ||
typeAnnotation.typeParameters.params[0], | ||
); | ||
return getPropertyType(name, optional, typeAnnotation.typeParameters.params[0]); | ||
case 'ObjectTypeAnnotation': | ||
@@ -88,5 +79,6 @@ return { | ||
type: 'ObjectTypeAnnotation', | ||
properties: typeAnnotation.properties.map(buildPropertiesForEvent), | ||
}, | ||
properties: typeAnnotation.properties.map(buildPropertiesForEvent) | ||
} | ||
}; | ||
case 'UnionTypeAnnotation': | ||
@@ -98,7 +90,8 @@ return { | ||
type: 'StringEnumTypeAnnotation', | ||
options: typeAnnotation.types.map(option => option.value), | ||
}, | ||
options: typeAnnotation.types.map(option => option.value) | ||
} | ||
}; | ||
default: | ||
(type ); | ||
type; | ||
throw new Error(`Unable to determine event type for "${name}": ${type}`); | ||
@@ -108,12 +101,9 @@ } | ||
function findEventArgumentsAndType( | ||
typeAnnotation, | ||
types, | ||
bubblingType, | ||
paperName, | ||
) { | ||
function findEventArgumentsAndType(typeAnnotation, types, bubblingType, paperName) { | ||
if (!typeAnnotation.id) { | ||
throw new Error("typeAnnotation of event doesn't have a name"); | ||
} | ||
const name = typeAnnotation.id.name; | ||
if (name === '$ReadOnly') { | ||
@@ -123,33 +113,19 @@ return { | ||
paperTopLevelNameDeprecated: paperName, | ||
bubblingType, | ||
bubblingType | ||
}; | ||
} else if (name === 'BubblingEventHandler' || name === 'DirectEventHandler') { | ||
const eventType = name === 'BubblingEventHandler' ? 'bubble' : 'direct'; | ||
const paperTopLevelNameDeprecated = | ||
typeAnnotation.typeParameters.params.length > 1 | ||
? typeAnnotation.typeParameters.params[1].value | ||
: null; | ||
if ( | ||
typeAnnotation.typeParameters.params[0].type === | ||
'NullLiteralTypeAnnotation' | ||
) { | ||
const paperTopLevelNameDeprecated = typeAnnotation.typeParameters.params.length > 1 ? typeAnnotation.typeParameters.params[1].value : null; | ||
if (typeAnnotation.typeParameters.params[0].type === 'NullLiteralTypeAnnotation') { | ||
return { | ||
argumentProps: [], | ||
bubblingType: eventType, | ||
paperTopLevelNameDeprecated, | ||
paperTopLevelNameDeprecated | ||
}; | ||
} | ||
return findEventArgumentsAndType( | ||
typeAnnotation.typeParameters.params[0], | ||
types, | ||
eventType, | ||
paperTopLevelNameDeprecated, | ||
); | ||
return findEventArgumentsAndType(typeAnnotation.typeParameters.params[0], types, eventType, paperTopLevelNameDeprecated); | ||
} else if (types[name]) { | ||
return findEventArgumentsAndType( | ||
types[name].right, | ||
types, | ||
bubblingType, | ||
paperName, | ||
); | ||
return findEventArgumentsAndType(types[name].right, types, bubblingType, paperName); | ||
} else { | ||
@@ -159,52 +135,40 @@ return { | ||
bubblingType: null, | ||
paperTopLevelNameDeprecated: null, | ||
paperTopLevelNameDeprecated: null | ||
}; | ||
} | ||
} | ||
/* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's | ||
* LTI update could not be added via codemod */ | ||
function buildPropertiesForEvent(property) { | ||
function buildPropertiesForEvent(property) { | ||
const name = property.key.name; | ||
const optional = | ||
property.value.type === 'NullableTypeAnnotation' || property.optional; | ||
let typeAnnotation = | ||
property.value.type === 'NullableTypeAnnotation' | ||
? property.value.typeAnnotation | ||
: property.value; | ||
const optional = property.value.type === 'NullableTypeAnnotation' || property.optional; | ||
let typeAnnotation = property.value.type === 'NullableTypeAnnotation' ? property.value.typeAnnotation : property.value; | ||
return getPropertyType(name, optional, typeAnnotation); | ||
} | ||
/* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's | ||
* LTI update could not be added via codemod */ | ||
function getEventArgument(argumentProps, name) { | ||
return { | ||
type: 'ObjectTypeAnnotation', | ||
properties: argumentProps.map(buildPropertiesForEvent), | ||
properties: argumentProps.map(buildPropertiesForEvent) | ||
}; | ||
} | ||
function buildEventSchema( | ||
types , | ||
property , | ||
) { | ||
function buildEventSchema(types, property) { | ||
const name = property.key.name; | ||
const optional = | ||
property.optional || property.value.type === 'NullableTypeAnnotation'; | ||
const optional = property.optional || property.value.type === 'NullableTypeAnnotation'; | ||
let typeAnnotation = property.value.type === 'NullableTypeAnnotation' ? property.value.typeAnnotation : property.value; | ||
let typeAnnotation = | ||
property.value.type === 'NullableTypeAnnotation' | ||
? property.value.typeAnnotation | ||
: property.value; | ||
if ( | ||
typeAnnotation.type !== 'GenericTypeAnnotation' || | ||
(typeAnnotation.id.name !== 'BubblingEventHandler' && | ||
typeAnnotation.id.name !== 'DirectEventHandler') | ||
) { | ||
if (typeAnnotation.type !== 'GenericTypeAnnotation' || typeAnnotation.id.name !== 'BubblingEventHandler' && typeAnnotation.id.name !== 'DirectEventHandler') { | ||
return null; | ||
} | ||
const { | ||
argumentProps, | ||
bubblingType, | ||
paperTopLevelNameDeprecated, | ||
} = findEventArgumentsAndType(typeAnnotation, types); | ||
const _findEventArgumentsAn = findEventArgumentsAndType(typeAnnotation, types), | ||
argumentProps = _findEventArgumentsAn.argumentProps, | ||
bubblingType = _findEventArgumentsAn.bubblingType, | ||
paperTopLevelNameDeprecated = _findEventArgumentsAn.paperTopLevelNameDeprecated; | ||
@@ -220,4 +184,4 @@ if (bubblingType && argumentProps) { | ||
type: 'EventTypeAnnotation', | ||
argument: getEventArgument(argumentProps, name), | ||
}, | ||
argument: getEventArgument(argumentProps, name) | ||
} | ||
}; | ||
@@ -232,4 +196,4 @@ } | ||
type: 'EventTypeAnnotation', | ||
argument: getEventArgument(argumentProps, name), | ||
}, | ||
argument: getEventArgument(argumentProps, name) | ||
} | ||
}; | ||
@@ -245,25 +209,11 @@ } | ||
} | ||
} | ||
} // $FlowFixMe[unclear-type] there's no flowtype for ASTs | ||
// $FlowFixMe[unclear-type] there's no flowtype for ASTs | ||
function getEvents( | ||
eventTypeAST , | ||
types , | ||
) { | ||
return eventTypeAST | ||
.filter(property => property.type === 'ObjectTypeProperty') | ||
.map(property => buildEventSchema(types, property)) | ||
.filter(Boolean); | ||
function getEvents(eventTypeAST, types) { | ||
return eventTypeAST.filter(property => property.type === 'ObjectTypeProperty').map(property => buildEventSchema(types, property)).filter(Boolean); | ||
} | ||
module.exports = { | ||
getEvents, | ||
}; | ||
getEvents | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,12 +10,9 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
function extendsForProp(prop , types ) { | ||
function extendsForProp(prop, types) { | ||
if (!prop.argument) { | ||
console.log('null', prop); | ||
} | ||
const name = prop.argument.id.name; | ||
@@ -32,32 +29,19 @@ | ||
type: 'ReactNativeBuiltInType', | ||
knownTypeName: 'ReactNativeCoreViewProps', | ||
knownTypeName: 'ReactNativeCoreViewProps' | ||
}; | ||
default: { | ||
throw new Error(`Unable to handle prop spread: ${name}`); | ||
} | ||
default: | ||
{ | ||
throw new Error(`Unable to handle prop spread: ${name}`); | ||
} | ||
} | ||
} | ||
function removeKnownExtends( | ||
typeDefinition , | ||
types , | ||
) { | ||
return typeDefinition.filter( | ||
prop => | ||
prop.type !== 'ObjectTypeSpreadProperty' || | ||
extendsForProp(prop, types) === null, | ||
); | ||
} | ||
function removeKnownExtends(typeDefinition, types) { | ||
return typeDefinition.filter(prop => prop.type !== 'ObjectTypeSpreadProperty' || extendsForProp(prop, types) === null); | ||
} // $FlowFixMe[unclear-type] there's no flowtype for ASTs | ||
// $FlowFixMe[unclear-type] there's no flowtype for ASTs | ||
function getExtendsProps( | ||
typeDefinition , | ||
types , | ||
) { | ||
return typeDefinition | ||
.filter(prop => prop.type === 'ObjectTypeSpreadProperty') | ||
.map(prop => extendsForProp(prop, types)) | ||
.filter(Boolean); | ||
function getExtendsProps(typeDefinition, types) { | ||
return typeDefinition.filter(prop => prop.type === 'ObjectTypeSpreadProperty').map(prop => extendsForProp(prop, types)).filter(Boolean); | ||
} | ||
@@ -67,3 +51,3 @@ | ||
getExtendsProps, | ||
removeKnownExtends, | ||
}; | ||
removeKnownExtends | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,25 +10,43 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
const {getCommands} = require('./commands'); | ||
const {getEvents} = require('./events'); | ||
const {getProps, getPropProperties} = require('./props'); | ||
const {getCommandOptions, getOptions} = require('./options'); | ||
const {getExtendsProps, removeKnownExtends} = require('./extends'); | ||
const {getTypes} = require('../utils'); | ||
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (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 = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
const _require = require('../utils'), | ||
getTypes = _require.getTypes; | ||
const _require2 = require('./commands'), | ||
getCommands = _require2.getCommands; | ||
const _require3 = require('./events'), | ||
getEvents = _require3.getEvents; | ||
const _require4 = require('./extends'), | ||
getExtendsProps = _require4.getExtendsProps, | ||
removeKnownExtends = _require4.removeKnownExtends; | ||
const _require5 = require('./options'), | ||
getCommandOptions = _require5.getCommandOptions, | ||
getOptions = _require5.getOptions; | ||
const _require6 = require('./props'), | ||
getProps = _require6.getProps; | ||
const _require7 = require('./componentsUtils.js'), | ||
getProperties = _require7.getProperties; | ||
/* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's | ||
* LTI update could not be added via codemod */ | ||
function findComponentConfig(ast) { | ||
const foundConfigs = []; | ||
const defaultExports = ast.body.filter( | ||
node => node.type === 'ExportDefaultDeclaration', | ||
); | ||
const defaultExports = ast.body.filter(node => node.type === 'ExportDefaultDeclaration'); | ||
defaultExports.forEach(statement => { | ||
let declaration = statement.declaration; | ||
let declaration = statement.declaration; // codegenNativeComponent can be nested inside a cast | ||
// expression so we need to go one level deeper | ||
// codegenNativeComponent can be nested inside a cast | ||
// expression so we need to go one level deeper | ||
if (declaration.type === 'TypeCastExpression') { | ||
@@ -42,13 +60,14 @@ declaration = declaration.expression; | ||
const funcArgumentParams = declaration.arguments; | ||
const nativeComponentType = { | ||
propsTypeName: typeArgumentParams[0].id.name, | ||
componentName: funcArgumentParams[0].value | ||
}; | ||
const nativeComponentType = {}; | ||
nativeComponentType.propsTypeName = typeArgumentParams[0].id.name; | ||
nativeComponentType.componentName = funcArgumentParams[0].value; | ||
if (funcArgumentParams.length > 1) { | ||
nativeComponentType.optionsExpression = funcArgumentParams[1]; | ||
} | ||
foundConfigs.push(nativeComponentType); | ||
} | ||
} catch (e) { | ||
// ignore | ||
} catch (e) {// ignore | ||
} | ||
@@ -60,2 +79,3 @@ }); | ||
} | ||
if (foundConfigs.length > 1) { | ||
@@ -66,43 +86,34 @@ throw new Error('Only one component is supported per file'); | ||
const foundConfig = foundConfigs[0]; | ||
const namedExports = ast.body.filter(node => node.type === 'ExportNamedDeclaration'); | ||
const commandsTypeNames = namedExports.map(statement => { | ||
let callExpression; | ||
let calleeName; | ||
const namedExports = ast.body.filter( | ||
node => node.type === 'ExportNamedDeclaration', | ||
); | ||
try { | ||
callExpression = statement.declaration.declarations[0].init; | ||
calleeName = callExpression.callee.name; | ||
} catch (e) { | ||
return; | ||
} | ||
const commandsTypeNames = namedExports | ||
.map(statement => { | ||
let callExpression; | ||
let calleeName; | ||
try { | ||
callExpression = statement.declaration.declarations[0].init; | ||
calleeName = callExpression.callee.name; | ||
} catch (e) { | ||
return; | ||
} | ||
if (calleeName !== 'codegenNativeCommands') { | ||
return; | ||
} // const statement.declaration.declarations[0].init | ||
if (calleeName !== 'codegenNativeCommands') { | ||
return; | ||
} | ||
// const statement.declaration.declarations[0].init | ||
if (callExpression.arguments.length !== 1) { | ||
throw new Error( | ||
'codegenNativeCommands must be passed options including the supported commands', | ||
); | ||
} | ||
if (callExpression.arguments.length !== 1) { | ||
throw new Error('codegenNativeCommands must be passed options including the supported commands'); | ||
} | ||
const typeArgumentParam = callExpression.typeArguments.params[0]; | ||
const typeArgumentParam = callExpression.typeArguments.params[0]; | ||
if (typeArgumentParam.type !== 'GenericTypeAnnotation') { | ||
throw new Error( | ||
"codegenNativeCommands doesn't support inline definitions. Specify a file local type alias", | ||
); | ||
} | ||
if (typeArgumentParam.type !== 'GenericTypeAnnotation') { | ||
throw new Error("codegenNativeCommands doesn't support inline definitions. Specify a file local type alias"); | ||
} | ||
return { | ||
commandTypeName: typeArgumentParam.id.name, | ||
commandOptionsExpression: callExpression.arguments[0], | ||
}; | ||
}) | ||
.filter(Boolean); | ||
return { | ||
commandTypeName: typeArgumentParam.id.name, | ||
commandOptionsExpression: callExpression.arguments[0] | ||
}; | ||
}).filter(Boolean); | ||
@@ -113,16 +124,12 @@ if (commandsTypeNames.length > 1) { | ||
return { | ||
...foundConfig, | ||
commandTypeName: | ||
commandsTypeNames[0] == null | ||
? null | ||
: commandsTypeNames[0].commandTypeName, | ||
commandOptionsExpression: | ||
commandsTypeNames[0] == null | ||
? null | ||
: commandsTypeNames[0].commandOptionsExpression, | ||
}; | ||
return _objectSpread({}, foundConfig, { | ||
commandTypeName: commandsTypeNames[0] == null ? null : commandsTypeNames[0].commandTypeName, | ||
commandOptionsExpression: commandsTypeNames[0] == null ? null : commandsTypeNames[0].commandOptionsExpression | ||
}); | ||
} | ||
function getCommandProperties(commandTypeName, types, commandOptions) { | ||
function getCommandProperties( | ||
/* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's | ||
* LTI update could not be added via codemod */ | ||
commandTypeName, types, commandOptions) { | ||
if (commandTypeName == null) { | ||
@@ -135,66 +142,44 @@ return []; | ||
if (typeAlias.type !== 'InterfaceDeclaration') { | ||
throw new Error( | ||
`The type argument for codegenNativeCommands must be an interface, received ${typeAlias.type}`, | ||
); | ||
throw new Error(`The type argument for codegenNativeCommands must be an interface, received ${typeAlias.type}`); | ||
} | ||
let properties; | ||
try { | ||
properties = typeAlias.body.properties; | ||
} catch (e) { | ||
throw new Error( | ||
`Failed to find type definition for "${commandTypeName}", please check that you have a valid codegen flow file`, | ||
); | ||
throw new Error(`Failed to find type definition for "${commandTypeName}", please check that you have a valid codegen flow file`); | ||
} | ||
const flowPropertyNames = properties | ||
.map(property => property && property.key && property.key.name) | ||
.filter(Boolean); | ||
const flowPropertyNames = properties.map(property => property && property.key && property.key.name).filter(Boolean); | ||
if (commandOptions == null || commandOptions.supportedCommands == null) { | ||
throw new Error( | ||
'codegenNativeCommands must be given an options object with supportedCommands array', | ||
); | ||
throw new Error('codegenNativeCommands must be given an options object with supportedCommands array'); | ||
} | ||
if ( | ||
commandOptions.supportedCommands.length !== flowPropertyNames.length || | ||
!commandOptions.supportedCommands.every(supportedCommand => | ||
flowPropertyNames.includes(supportedCommand), | ||
) | ||
) { | ||
throw new Error( | ||
`codegenNativeCommands expected the same supportedCommands specified in the ${commandTypeName} interface: ${flowPropertyNames.join( | ||
', ', | ||
)}`, | ||
); | ||
if (commandOptions.supportedCommands.length !== flowPropertyNames.length || !commandOptions.supportedCommands.every(supportedCommand => flowPropertyNames.includes(supportedCommand))) { | ||
throw new Error(`codegenNativeCommands expected the same supportedCommands specified in the ${commandTypeName} interface: ${flowPropertyNames.join(', ')}`); | ||
} | ||
return properties; | ||
} | ||
} // $FlowFixMe[signature-verification-failure] there's no flowtype for AST | ||
// $FlowFixMe[signature-verification-failure] there's no flowtype for AST | ||
function buildComponentSchema(ast) { | ||
const { | ||
componentName, | ||
propsTypeName, | ||
commandTypeName, | ||
commandOptionsExpression, | ||
optionsExpression, | ||
} = findComponentConfig(ast); | ||
/* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's | ||
* LTI update could not be added via codemod */ | ||
function buildComponentSchema(ast) { | ||
const _findComponentConfig = findComponentConfig(ast), | ||
componentName = _findComponentConfig.componentName, | ||
propsTypeName = _findComponentConfig.propsTypeName, | ||
commandTypeName = _findComponentConfig.commandTypeName, | ||
commandOptionsExpression = _findComponentConfig.commandOptionsExpression, | ||
optionsExpression = _findComponentConfig.optionsExpression; | ||
const types = getTypes(ast); | ||
const propProperties = getPropProperties(propsTypeName, types); | ||
const propProperties = getProperties(propsTypeName, types); | ||
const commandOptions = getCommandOptions(commandOptionsExpression); | ||
const commandProperties = getCommandProperties( | ||
commandTypeName, | ||
types, | ||
commandOptions, | ||
); | ||
const commandProperties = getCommandProperties(commandTypeName, types, commandOptions); | ||
const extendsProps = getExtendsProps(propProperties, types); | ||
const options = getOptions(optionsExpression); | ||
const nonExtendsProps = removeKnownExtends(propProperties, types); | ||
@@ -204,3 +189,2 @@ const props = getProps(nonExtendsProps, types); | ||
const commands = getCommands(commandProperties, types); | ||
return { | ||
@@ -213,3 +197,3 @@ filename: componentName, | ||
props, | ||
commands, | ||
commands | ||
}; | ||
@@ -219,3 +203,3 @@ } | ||
module.exports = { | ||
buildComponentSchema, | ||
}; | ||
buildComponentSchema | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,17 +10,5 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; // $FlowFixMe[unclear-type] there's no flowtype for ASTs | ||
'use strict'; | ||
// $FlowFixMe[unclear-type] there's no flowtype for ASTs | ||
function getCommandOptions( | ||
commandOptionsExpression , | ||
) { | ||
function getCommandOptions(commandOptionsExpression) { | ||
if (commandOptionsExpression == null) { | ||
@@ -31,17 +19,10 @@ return null; | ||
let foundOptions; | ||
try { | ||
foundOptions = commandOptionsExpression.properties.reduce( | ||
(options, prop) => { | ||
options[prop.key.name] = ( | ||
(prop && prop.value && prop.value.elements) || | ||
[] | ||
).map(element => element && element.value); | ||
return options; | ||
}, | ||
{}, | ||
); | ||
foundOptions = commandOptionsExpression.properties.reduce((options, prop) => { | ||
options[prop.key.name] = (prop && prop.value && prop.value.elements || []).map(element => element && element.value); | ||
return options; | ||
}, {}); | ||
} catch (e) { | ||
throw new Error( | ||
'Failed to parse command options, please check that they are defined correctly', | ||
); | ||
throw new Error('Failed to parse command options, please check that they are defined correctly'); | ||
} | ||
@@ -52,31 +33,25 @@ | ||
function getOptions(optionsExpression ) { | ||
function getOptions(optionsExpression) { | ||
if (!optionsExpression) { | ||
return null; | ||
} | ||
let foundOptions; | ||
try { | ||
foundOptions = optionsExpression.properties.reduce((options, prop) => { | ||
if (prop.value.type === 'ArrayExpression') { | ||
options[prop.key.name] = prop.value.elements.map( | ||
element => element.value, | ||
); | ||
options[prop.key.name] = prop.value.elements.map(element => element.value); | ||
} else { | ||
options[prop.key.name] = prop.value.value; | ||
} | ||
return options; | ||
}, {}); | ||
} catch (e) { | ||
throw new Error( | ||
'Failed to parse codegen options, please check that they are defined correctly', | ||
); | ||
throw new Error('Failed to parse codegen options, please check that they are defined correctly'); | ||
} | ||
if ( | ||
foundOptions.paperComponentName && | ||
foundOptions.paperComponentNameDeprecated | ||
) { | ||
throw new Error( | ||
'Failed to parse codegen options, cannot use both paperComponentName and paperComponentNameDeprecated', | ||
); | ||
if (foundOptions.paperComponentName && foundOptions.paperComponentNameDeprecated) { | ||
throw new Error('Failed to parse codegen options, cannot use both paperComponentName and paperComponentNameDeprecated'); | ||
} | ||
@@ -89,3 +64,3 @@ | ||
getCommandOptions, | ||
getOptions, | ||
}; | ||
getOptions | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,467 +10,35 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
const {getValueFromTypes} = require('../utils.js'); | ||
const _require = require('./componentsUtils.js'), | ||
flattenProperties = _require.flattenProperties, | ||
getSchemaInfo = _require.getSchemaInfo, | ||
getTypeAnnotation = _require.getTypeAnnotation; // $FlowFixMe[unclear-type] there's no flowtype for ASTs | ||
function getPropProperties( | ||
propsTypeName , | ||
types , | ||
) { | ||
const typeAlias = types[propsTypeName]; | ||
try { | ||
return typeAlias.right.typeParameters.params[0].properties; | ||
} catch (e) { | ||
throw new Error( | ||
`Failed to find type definition for "${propsTypeName}", please check that you have a valid codegen flow file`, | ||
); | ||
} | ||
} | ||
function buildPropSchema(property, types) { | ||
const info = getSchemaInfo(property, types); | ||
function getTypeAnnotationForArray(name, typeAnnotation, defaultValue, types) { | ||
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' && | ||
extractedTypeAnnotation.id.name === 'WithDefault' | ||
) { | ||
throw new Error( | ||
'Nested defaults such as "$ReadOnlyArray<WithDefault<boolean, false>>" are not supported, please declare defaults at the top level of value definitions as in "WithDefault<$ReadOnlyArray<boolean>, false>"', | ||
); | ||
} | ||
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, | ||
) | ||
.map(prop => buildPropSchema(prop, types)) | ||
.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, | ||
) | ||
.map(prop => buildPropSchema(prop, types)) | ||
.filter(Boolean), | ||
}, | ||
}; | ||
} | ||
} | ||
const type = | ||
extractedTypeAnnotation.type === 'GenericTypeAnnotation' | ||
? extractedTypeAnnotation.id.name | ||
: extractedTypeAnnotation.type; | ||
switch (type) { | ||
case 'ImageSource': | ||
return { | ||
type: 'ReservedPropTypeAnnotation', | ||
name: 'ImageSourcePrimitive', | ||
}; | ||
case 'ColorValue': | ||
case 'ProcessedColorValue': | ||
return { | ||
type: 'ReservedPropTypeAnnotation', | ||
name: 'ColorPrimitive', | ||
}; | ||
case 'PointValue': | ||
return { | ||
type: 'ReservedPropTypeAnnotation', | ||
name: 'PointPrimitive', | ||
}; | ||
case 'EdgeInsetsValue': | ||
return { | ||
type: 'ReservedPropTypeAnnotation', | ||
name: 'EdgeInsetsPrimitive', | ||
}; | ||
case 'Stringish': | ||
return { | ||
type: 'StringTypeAnnotation', | ||
}; | ||
case 'Int32': | ||
return { | ||
type: 'Int32TypeAnnotation', | ||
}; | ||
case 'Double': | ||
return { | ||
type: 'DoubleTypeAnnotation', | ||
}; | ||
case 'Float': | ||
return { | ||
type: 'FloatTypeAnnotation', | ||
}; | ||
case '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}", recieved "${unionType}"`, | ||
); | ||
} | ||
default: | ||
(type ); | ||
throw new Error(`Unknown prop type for "${name}": ${type}`); | ||
} | ||
} | ||
function getTypeAnnotation( | ||
name, | ||
annotation, | ||
defaultValue, | ||
withNullDefault, | ||
types, | ||
) { | ||
const typeAnnotation = getValueFromTypes(annotation, types); | ||
if ( | ||
typeAnnotation.type === 'GenericTypeAnnotation' && | ||
typeAnnotation.id.name === '$ReadOnlyArray' | ||
) { | ||
return { | ||
type: 'ArrayTypeAnnotation', | ||
elementType: getTypeAnnotationForArray( | ||
name, | ||
typeAnnotation.typeParameters.params[0], | ||
defaultValue, | ||
types, | ||
), | ||
}; | ||
} | ||
if ( | ||
typeAnnotation.type === 'GenericTypeAnnotation' && | ||
typeAnnotation.id.name === '$ReadOnly' | ||
) { | ||
return { | ||
type: 'ObjectTypeAnnotation', | ||
properties: flattenProperties( | ||
typeAnnotation.typeParameters.params[0].properties, | ||
types, | ||
) | ||
.map(prop => buildPropSchema(prop, types)) | ||
.filter(Boolean), | ||
}; | ||
} | ||
const type = | ||
typeAnnotation.type === 'GenericTypeAnnotation' | ||
? typeAnnotation.id.name | ||
: typeAnnotation.type; | ||
switch (type) { | ||
case 'ImageSource': | ||
return { | ||
type: 'ReservedPropTypeAnnotation', | ||
name: 'ImageSourcePrimitive', | ||
}; | ||
case 'ColorValue': | ||
case 'ProcessedColorValue': | ||
return { | ||
type: 'ReservedPropTypeAnnotation', | ||
name: 'ColorPrimitive', | ||
}; | ||
case 'ColorArrayValue': | ||
return { | ||
type: 'ArrayTypeAnnotation', | ||
elementType: { | ||
type: 'ReservedPropTypeAnnotation', | ||
name: 'ColorPrimitive', | ||
}, | ||
}; | ||
case 'PointValue': | ||
return { | ||
type: 'ReservedPropTypeAnnotation', | ||
name: 'PointPrimitive', | ||
}; | ||
case 'EdgeInsetsValue': | ||
return { | ||
type: 'ReservedPropTypeAnnotation', | ||
name: 'EdgeInsetsPrimitive', | ||
}; | ||
case 'Int32': | ||
return { | ||
type: 'Int32TypeAnnotation', | ||
default: ((defaultValue ? defaultValue : 0) ), | ||
}; | ||
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 'NumberTypeAnnotation': | ||
throw new Error( | ||
`Cannot use "${type}" type annotation for "${name}": must use a specific numeric type like Int32, Double, or Float`, | ||
); | ||
default: | ||
(type ); | ||
throw new Error(`Unknown prop type for "${name}": "${type}"`); | ||
} | ||
} | ||
function buildPropSchema( | ||
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') | ||
) { | ||
if (info == null) { | ||
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; | ||
} | ||
} | ||
const name = info.name, | ||
optional = info.optional, | ||
typeAnnotation = info.typeAnnotation, | ||
defaultValue = info.defaultValue, | ||
withNullDefault = info.withNullDefault; | ||
return { | ||
name, | ||
optional, | ||
typeAnnotation: getTypeAnnotation( | ||
name, | ||
typeAnnotation, | ||
defaultValue, | ||
withNullDefault, | ||
types, | ||
), | ||
typeAnnotation: getTypeAnnotation(name, typeAnnotation, defaultValue, withNullDefault, types, buildPropSchema) | ||
}; | ||
} | ||
// $FlowFixMe[unclear-type] there's no flowtype for ASTs | ||
function verifyPropNotAlreadyDefined( | ||
props , | ||
needleProp , | ||
) { | ||
const propName = needleProp.key.name; | ||
const foundProp = props.some(prop => prop.key.name === propName); | ||
if (foundProp) { | ||
throw new Error(`A prop was already defined with the name ${propName}`); | ||
} | ||
function getProps(typeDefinition, types) { | ||
return flattenProperties(typeDefinition, types).map(property => buildPropSchema(property, types)).filter(Boolean); | ||
} | ||
function flattenProperties( | ||
typeDefinition , | ||
types , | ||
) { | ||
return typeDefinition | ||
.map(property => { | ||
if (property.type === 'ObjectTypeProperty') { | ||
return property; | ||
} else if (property.type === 'ObjectTypeSpreadProperty') { | ||
return flattenProperties( | ||
getPropProperties(property.argument.id.name, types), | ||
types, | ||
); | ||
} | ||
}) | ||
.reduce((acc, item) => { | ||
if (Array.isArray(item)) { | ||
item.forEach(prop => { | ||
verifyPropNotAlreadyDefined(acc, prop); | ||
}); | ||
return acc.concat(item); | ||
} else { | ||
verifyPropNotAlreadyDefined(acc, item); | ||
acc.push(item); | ||
return acc; | ||
} | ||
}, []) | ||
.filter(Boolean); | ||
} | ||
function getProps( | ||
typeDefinition , | ||
types , | ||
) { | ||
return flattenProperties(typeDefinition, types) | ||
.map(property => buildPropSchema(property, types)) | ||
.filter(Boolean); | ||
} | ||
module.exports = { | ||
getProps, | ||
getPropProperties, | ||
}; | ||
getProps | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -7,28 +7,13 @@ * This source code is licensed under the MIT license found in the | ||
* | ||
* strict | ||
* @format | ||
* strict-local | ||
*/ | ||
'use strict'; | ||
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (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 = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
function wrapComponentSchema({ | ||
@@ -41,4 +26,4 @@ filename, | ||
options, | ||
commands, | ||
} ) { | ||
commands | ||
}) { | ||
return { | ||
@@ -49,12 +34,11 @@ modules: { | ||
components: { | ||
[componentName]: { | ||
...(options || {}), | ||
[componentName]: _objectSpread({}, options || {}, { | ||
extendsProps, | ||
events, | ||
props, | ||
commands, | ||
}, | ||
}, | ||
}, | ||
}, | ||
commands | ||
}) | ||
} | ||
} | ||
} | ||
}; | ||
@@ -64,3 +48,3 @@ } | ||
module.exports = { | ||
wrapComponentSchema, | ||
}; | ||
wrapComponentSchema | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,127 +10,64 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; // $FlowFixMe[untyped-import] there's no flowtype flow-parser | ||
'use strict'; | ||
const flowParser = require('flow-parser'); | ||
// $FlowFixMe[untyped-import] there's no flowtype flow-parser | ||
const flowParser = require('flow-parser'); | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const {buildComponentSchema} = require('./components'); | ||
const {wrapComponentSchema} = require('./components/schema'); | ||
const {buildModuleSchema} = require('./modules'); | ||
const {wrapModuleSchema} = require('./modules/schema'); | ||
const { | ||
createParserErrorCapturer, | ||
visit, | ||
isModuleRegistryCall, | ||
} = require('./utils'); | ||
const invariant = require('invariant'); | ||
function getConfigType( | ||
// TODO(T71778680): Flow-type this node. | ||
ast , | ||
) { | ||
let isComponent = false; | ||
let isModule = false; | ||
const _require = require('../utils'), | ||
buildSchemaFromConfigType = _require.buildSchemaFromConfigType, | ||
getConfigType = _require.getConfigType, | ||
isModuleRegistryCall = _require.isModuleRegistryCall; | ||
visit(ast, { | ||
const _require2 = require('./components'), | ||
buildComponentSchema = _require2.buildComponentSchema; | ||
const _require3 = require('./components/schema'), | ||
wrapComponentSchema = _require3.wrapComponentSchema; | ||
const _require4 = require('./modules'), | ||
buildModuleSchema = _require4.buildModuleSchema; | ||
function Visitor(infoMap) { | ||
return { | ||
CallExpression(node) { | ||
if ( | ||
node.callee.type === 'Identifier' && | ||
node.callee.name === 'codegenNativeComponent' | ||
) { | ||
isComponent = true; | ||
if (node.callee.type === 'Identifier' && node.callee.name === 'codegenNativeComponent') { | ||
infoMap.isComponent = true; | ||
} | ||
if (isModuleRegistryCall(node)) { | ||
isModule = true; | ||
infoMap.isModule = true; | ||
} | ||
}, | ||
InterfaceExtends(node) { | ||
if (node.id.name === 'TurboModule') { | ||
isModule = true; | ||
infoMap.isModule = true; | ||
} | ||
}, | ||
}); | ||
} | ||
if (isModule && isComponent) { | ||
throw new Error( | ||
'Found type extending "TurboModule" and exported "codegenNativeComponent" declaration in one file. Split them into separated files.', | ||
); | ||
} | ||
if (isModule) { | ||
return 'module'; | ||
} else if (isComponent) { | ||
return 'component'; | ||
} else { | ||
return 'none'; | ||
} | ||
}; | ||
} | ||
function buildSchema(contents , filename ) { | ||
function buildSchema(contents, filename) { | ||
// Early return for non-Spec JavaScript files | ||
if ( | ||
!contents.includes('codegenNativeComponent') && | ||
!contents.includes('TurboModule') | ||
) { | ||
return {modules: {}}; | ||
if (!contents.includes('codegenNativeComponent') && !contents.includes('TurboModule')) { | ||
return { | ||
modules: {} | ||
}; | ||
} | ||
const ast = flowParser.parse(contents); | ||
const configType = getConfigType(ast); | ||
switch (configType) { | ||
case 'component': { | ||
return wrapComponentSchema(buildComponentSchema(ast)); | ||
} | ||
case 'module': { | ||
if (filename === undefined || filename === null) { | ||
throw new Error('Filepath expected while parasing a module'); | ||
} | ||
const hasteModuleName = path.basename(filename).replace(/\.js$/, ''); | ||
const [parsingErrors, tryParse] = createParserErrorCapturer(); | ||
const schema = tryParse(() => | ||
buildModuleSchema(hasteModuleName, ast, tryParse), | ||
); | ||
if (parsingErrors.length > 0) { | ||
/** | ||
* TODO(T77968131): We have two options: | ||
* - Throw the first error, but indicate there are more then one errors. | ||
* - Display all errors, nicely formatted. | ||
* | ||
* For the time being, we're just throw the first error. | ||
**/ | ||
throw parsingErrors[0]; | ||
} | ||
invariant( | ||
schema != null, | ||
'When there are no parsing errors, the schema should not be null', | ||
); | ||
return wrapModuleSchema(schema, hasteModuleName); | ||
} | ||
default: | ||
return {modules: {}}; | ||
} | ||
const ast = flowParser.parse(contents, { | ||
enums: true | ||
}); | ||
const configType = getConfigType(ast, Visitor); | ||
return buildSchemaFromConfigType(configType, filename, ast, wrapComponentSchema, buildComponentSchema, buildModuleSchema); | ||
} | ||
function parseFile(filename ) { | ||
function parseModuleFixture(filename) { | ||
const contents = fs.readFileSync(filename, 'utf8'); | ||
return buildSchema(contents, filename); | ||
} | ||
function parseModuleFixture(filename ) { | ||
const contents = fs.readFileSync(filename, 'utf8'); | ||
return buildSchema(contents, 'path/NativeSampleTurboModule.js'); | ||
} | ||
function parseString(contents , filename ) { | ||
function parseString(contents, filename) { | ||
return buildSchema(contents, filename); | ||
@@ -140,5 +77,5 @@ } | ||
module.exports = { | ||
parseFile, | ||
buildSchema, | ||
parseModuleFixture, | ||
parseString, | ||
}; | ||
parseString | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,526 +10,276 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (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 = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } | ||
const { | ||
resolveTypeAnnotation, | ||
getTypes, | ||
visit, | ||
isModuleRegistryCall, | ||
} = require('../utils.js'); | ||
const {unwrapNullable, wrapNullable} = require('./utils'); | ||
const { | ||
IncorrectlyParameterizedFlowGenericParserError, | ||
MisnamedModuleFlowInterfaceParserError, | ||
ModuleFlowInterfaceNotFoundParserError, | ||
MoreThanOneModuleFlowInterfaceParserError, | ||
UnnamedFunctionParamParserError, | ||
UnsupportedArrayElementTypeAnnotationParserError, | ||
UnsupportedFlowGenericParserError, | ||
UnsupportedFlowTypeAnnotationParserError, | ||
UnsupportedFunctionParamTypeAnnotationParserError, | ||
UnsupportedFunctionReturnTypeAnnotationParserError, | ||
UnsupportedModulePropertyParserError, | ||
UnsupportedObjectPropertyTypeAnnotationParserError, | ||
UnsupportedObjectPropertyValueTypeAnnotationParserError, | ||
UnusedModuleFlowInterfaceParserError, | ||
MoreThanOneModuleRegistryCallsParserError, | ||
UntypedModuleRegistryCallParserError, | ||
IncorrectModuleRegistryCallTypeParameterParserError, | ||
IncorrectModuleRegistryCallArityParserError, | ||
IncorrectModuleRegistryCallArgumentTypeParserError, | ||
} = require('./errors.js'); | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
const invariant = require('invariant'); | ||
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); } | ||
function nullGuard (fn ) { | ||
return fn(); | ||
} | ||
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } | ||
function translateTypeAnnotation( | ||
hasteModuleName , | ||
/** | ||
* TODO(T71778680): Flow-type this node. | ||
*/ | ||
flowTypeAnnotation , | ||
types , | ||
aliasMap , | ||
tryParse , | ||
) { | ||
const { | ||
nullable, | ||
typeAnnotation, | ||
typeAliasResolutionStatus, | ||
} = resolveTypeAnnotation(flowTypeAnnotation, types); | ||
function _iterableToArrayLimit(arr, i) { if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } | ||
switch (typeAnnotation.type) { | ||
case 'GenericTypeAnnotation': { | ||
switch (typeAnnotation.id.name) { | ||
case 'RootTag': { | ||
return wrapNullable(nullable, { | ||
type: 'ReservedTypeAnnotation', | ||
name: 'RootTag', | ||
}); | ||
} | ||
case 'Promise': { | ||
assertGenericTypeAnnotationHasExactlyOneTypeParameter( | ||
hasteModuleName, | ||
typeAnnotation, | ||
); | ||
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } | ||
return wrapNullable(nullable, { | ||
type: 'PromiseTypeAnnotation', | ||
}); | ||
} | ||
case 'Array': | ||
case '$ReadOnlyArray': { | ||
assertGenericTypeAnnotationHasExactlyOneTypeParameter( | ||
hasteModuleName, | ||
typeAnnotation, | ||
); | ||
const _require = require('../../parsers-utils'), | ||
nullGuard = _require.nullGuard; | ||
try { | ||
/** | ||
* TODO(T72031674): Migrate all our NativeModule specs to not use | ||
* invalid Array ElementTypes. Then, make the elementType a required | ||
* parameter. | ||
*/ | ||
const [elementType, isElementTypeNullable] = unwrapNullable( | ||
translateTypeAnnotation( | ||
hasteModuleName, | ||
typeAnnotation.typeParameters.params[0], | ||
types, | ||
aliasMap, | ||
/** | ||
* TODO(T72031674): Ensure that all ParsingErrors that are thrown | ||
* while parsing the array element don't get captured and collected. | ||
* Why? If we detect any parsing error while parsing the element, | ||
* we should default it to null down the line, here. This is | ||
* the correct behaviour until we migrate all our NativeModule specs | ||
* to be parseable. | ||
*/ | ||
nullGuard, | ||
), | ||
); | ||
const _require2 = require('../../utils'), | ||
visit = _require2.visit, | ||
verifyPlatforms = _require2.verifyPlatforms, | ||
isModuleRegistryCall = _require2.isModuleRegistryCall; | ||
if (elementType.type === 'VoidTypeAnnotation') { | ||
throw new UnsupportedArrayElementTypeAnnotationParserError( | ||
hasteModuleName, | ||
typeAnnotation.typeParameters.params[0], | ||
typeAnnotation.type, | ||
'void', | ||
); | ||
} | ||
const _require3 = require('../utils.js'), | ||
resolveTypeAnnotation = _require3.resolveTypeAnnotation, | ||
getTypes = _require3.getTypes; | ||
if (elementType.type === 'PromiseTypeAnnotation') { | ||
throw new UnsupportedArrayElementTypeAnnotationParserError( | ||
hasteModuleName, | ||
typeAnnotation.typeParameters.params[0], | ||
typeAnnotation.type, | ||
'Promise', | ||
); | ||
} | ||
const _require4 = require('../../parsers-commons'), | ||
unwrapNullable = _require4.unwrapNullable, | ||
wrapNullable = _require4.wrapNullable, | ||
assertGenericTypeAnnotationHasExactlyOneTypeParameter = _require4.assertGenericTypeAnnotationHasExactlyOneTypeParameter, | ||
parseObjectProperty = _require4.parseObjectProperty, | ||
emitUnionTypeAnnotation = _require4.emitUnionTypeAnnotation, | ||
translateDefault = _require4.translateDefault, | ||
translateFunctionTypeAnnotation = _require4.translateFunctionTypeAnnotation; | ||
if (elementType.type === 'FunctionTypeAnnotation') { | ||
throw new UnsupportedArrayElementTypeAnnotationParserError( | ||
hasteModuleName, | ||
typeAnnotation.typeParameters.params[0], | ||
typeAnnotation.type, | ||
'FunctionTypeAnnotation', | ||
); | ||
} | ||
const _require5 = require('../../parsers-primitives'), | ||
emitBoolean = _require5.emitBoolean, | ||
emitDouble = _require5.emitDouble, | ||
emitFloat = _require5.emitFloat, | ||
emitFunction = _require5.emitFunction, | ||
emitNumber = _require5.emitNumber, | ||
emitInt32 = _require5.emitInt32, | ||
emitObject = _require5.emitObject, | ||
emitPromise = _require5.emitPromise, | ||
emitRootTag = _require5.emitRootTag, | ||
emitVoid = _require5.emitVoid, | ||
emitString = _require5.emitString, | ||
emitStringish = _require5.emitStringish, | ||
emitMixedTypeAnnotation = _require5.emitMixedTypeAnnotation, | ||
typeAliasResolution = _require5.typeAliasResolution; | ||
const finalTypeAnnotation | ||
= { | ||
type: 'ArrayTypeAnnotation', | ||
elementType: wrapNullable(isElementTypeNullable, elementType), | ||
}; | ||
const _require6 = require('../../errors.js'), | ||
UnsupportedArrayElementTypeAnnotationParserError = _require6.UnsupportedArrayElementTypeAnnotationParserError, | ||
UnsupportedTypeAnnotationParserError = _require6.UnsupportedTypeAnnotationParserError, | ||
IncorrectModuleRegistryCallArgumentTypeParserError = _require6.IncorrectModuleRegistryCallArgumentTypeParserError; | ||
return wrapNullable(nullable, finalTypeAnnotation); | ||
} catch (ex) { | ||
return wrapNullable(nullable, { | ||
type: 'ArrayTypeAnnotation', | ||
}); | ||
} | ||
} | ||
case '$ReadOnly': { | ||
assertGenericTypeAnnotationHasExactlyOneTypeParameter( | ||
hasteModuleName, | ||
typeAnnotation, | ||
); | ||
const _require7 = require('../../error-utils'), | ||
throwIfModuleInterfaceNotFound = _require7.throwIfModuleInterfaceNotFound, | ||
throwIfModuleInterfaceIsMisnamed = _require7.throwIfModuleInterfaceIsMisnamed, | ||
throwIfUnusedModuleInterfaceParserError = _require7.throwIfUnusedModuleInterfaceParserError, | ||
throwIfWrongNumberOfCallExpressionArgs = _require7.throwIfWrongNumberOfCallExpressionArgs, | ||
throwIfMoreThanOneModuleRegistryCalls = _require7.throwIfMoreThanOneModuleRegistryCalls, | ||
throwIfIncorrectModuleRegistryCallTypeParameterParserError = _require7.throwIfIncorrectModuleRegistryCallTypeParameterParserError, | ||
throwIfUntypedModule = _require7.throwIfUntypedModule, | ||
throwIfModuleTypeIsUnsupported = _require7.throwIfModuleTypeIsUnsupported, | ||
throwIfMoreThanOneModuleInterfaceParserError = _require7.throwIfMoreThanOneModuleInterfaceParserError; | ||
return translateTypeAnnotation( | ||
hasteModuleName, | ||
typeAnnotation.typeParameters.params[0], | ||
types, | ||
aliasMap, | ||
tryParse, | ||
); | ||
} | ||
case 'Stringish': { | ||
return wrapNullable(nullable, { | ||
type: 'StringTypeAnnotation', | ||
}); | ||
} | ||
case 'Int32': { | ||
return wrapNullable(nullable, { | ||
type: 'Int32TypeAnnotation', | ||
}); | ||
} | ||
case 'Double': { | ||
return wrapNullable(nullable, { | ||
type: 'DoubleTypeAnnotation', | ||
}); | ||
} | ||
case 'Float': { | ||
return wrapNullable(nullable, { | ||
type: 'FloatTypeAnnotation', | ||
}); | ||
} | ||
case 'UnsafeObject': | ||
case 'Object': { | ||
return wrapNullable(nullable, { | ||
type: 'GenericObjectTypeAnnotation', | ||
}); | ||
} | ||
default: { | ||
throw new UnsupportedFlowGenericParserError( | ||
hasteModuleName, | ||
typeAnnotation, | ||
); | ||
} | ||
} | ||
const _require8 = require('../parser.js'), | ||
FlowParser = _require8.FlowParser; | ||
const language = 'Flow'; | ||
const parser = new FlowParser(); | ||
function translateArrayTypeAnnotation(hasteModuleName, types, aliasMap, cxxOnly, flowArrayType, flowElementType, nullable) { | ||
try { | ||
/** | ||
* TODO(T72031674): Migrate all our NativeModule specs to not use | ||
* invalid Array ElementTypes. Then, make the elementType a required | ||
* parameter. | ||
*/ | ||
const _unwrapNullable = unwrapNullable(translateTypeAnnotation(hasteModuleName, flowElementType, types, aliasMap, | ||
/** | ||
* TODO(T72031674): Ensure that all ParsingErrors that are thrown | ||
* while parsing the array element don't get captured and collected. | ||
* Why? If we detect any parsing error while parsing the element, | ||
* we should default it to null down the line, here. This is | ||
* the correct behaviour until we migrate all our NativeModule specs | ||
* to be parseable. | ||
*/ | ||
nullGuard, cxxOnly)), | ||
_unwrapNullable2 = _slicedToArray(_unwrapNullable, 2), | ||
elementType = _unwrapNullable2[0], | ||
isElementTypeNullable = _unwrapNullable2[1]; | ||
if (elementType.type === 'VoidTypeAnnotation') { | ||
throw new UnsupportedArrayElementTypeAnnotationParserError(hasteModuleName, flowElementType, flowArrayType, 'void', language); | ||
} | ||
case 'ObjectTypeAnnotation': { | ||
const objectTypeAnnotation = { | ||
type: 'ObjectTypeAnnotation', | ||
// $FlowFixMe[missing-type-arg] | ||
properties: (typeAnnotation.properties ) | ||
.map ( | ||
property => { | ||
return tryParse(() => { | ||
if (property.type !== 'ObjectTypeProperty') { | ||
throw new UnsupportedObjectPropertyTypeAnnotationParserError( | ||
hasteModuleName, | ||
property, | ||
property.type, | ||
); | ||
} | ||
const {optional, key} = property; | ||
if (elementType.type === 'PromiseTypeAnnotation') { | ||
throw new UnsupportedArrayElementTypeAnnotationParserError(hasteModuleName, flowElementType, flowArrayType, 'Promise', language); | ||
} | ||
const [ | ||
propertyTypeAnnotation, | ||
isPropertyNullable, | ||
] = unwrapNullable( | ||
translateTypeAnnotation( | ||
hasteModuleName, | ||
property.value, | ||
types, | ||
aliasMap, | ||
tryParse, | ||
), | ||
); | ||
if (elementType.type === 'FunctionTypeAnnotation') { | ||
throw new UnsupportedArrayElementTypeAnnotationParserError(hasteModuleName, flowElementType, flowArrayType, 'FunctionTypeAnnotation', language); | ||
} | ||
if (propertyTypeAnnotation.type === 'FunctionTypeAnnotation') { | ||
throw new UnsupportedObjectPropertyValueTypeAnnotationParserError( | ||
hasteModuleName, | ||
property.value, | ||
property.key, | ||
propertyTypeAnnotation.type, | ||
); | ||
} | ||
const finalTypeAnnotation = { | ||
type: 'ArrayTypeAnnotation', | ||
elementType: wrapNullable(isElementTypeNullable, elementType) | ||
}; | ||
return wrapNullable(nullable, finalTypeAnnotation); | ||
} catch (ex) { | ||
return wrapNullable(nullable, { | ||
type: 'ArrayTypeAnnotation' | ||
}); | ||
} | ||
} | ||
if (propertyTypeAnnotation.type === 'VoidTypeAnnotation') { | ||
throw new UnsupportedObjectPropertyValueTypeAnnotationParserError( | ||
hasteModuleName, | ||
property.value, | ||
property.key, | ||
'void', | ||
); | ||
} | ||
function translateTypeAnnotation(hasteModuleName, | ||
/** | ||
* TODO(T71778680): Flow-type this node. | ||
*/ | ||
flowTypeAnnotation, types, aliasMap, tryParse, cxxOnly) { | ||
const _resolveTypeAnnotatio = resolveTypeAnnotation(flowTypeAnnotation, types), | ||
nullable = _resolveTypeAnnotatio.nullable, | ||
typeAnnotation = _resolveTypeAnnotatio.typeAnnotation, | ||
typeAliasResolutionStatus = _resolveTypeAnnotatio.typeAliasResolutionStatus; | ||
if (propertyTypeAnnotation.type === 'PromiseTypeAnnotation') { | ||
throw new UnsupportedObjectPropertyValueTypeAnnotationParserError( | ||
hasteModuleName, | ||
property.value, | ||
property.key, | ||
'Promise', | ||
); | ||
} | ||
switch (typeAnnotation.type) { | ||
case 'GenericTypeAnnotation': | ||
{ | ||
switch (typeAnnotation.id.name) { | ||
case 'RootTag': | ||
{ | ||
return emitRootTag(nullable); | ||
} | ||
return { | ||
name: key.name, | ||
optional, | ||
typeAnnotation: wrapNullable( | ||
isPropertyNullable, | ||
propertyTypeAnnotation, | ||
), | ||
}; | ||
}); | ||
}, | ||
) | ||
.filter(Boolean), | ||
}; | ||
case 'Promise': | ||
{ | ||
return emitPromise(hasteModuleName, typeAnnotation, parser, nullable); | ||
} | ||
if (!typeAliasResolutionStatus.successful) { | ||
return wrapNullable(nullable, objectTypeAnnotation); | ||
} | ||
case 'Array': | ||
case '$ReadOnlyArray': | ||
{ | ||
assertGenericTypeAnnotationHasExactlyOneTypeParameter(hasteModuleName, typeAnnotation, parser); | ||
return translateArrayTypeAnnotation(hasteModuleName, types, aliasMap, cxxOnly, typeAnnotation.type, typeAnnotation.typeParameters.params[0], nullable); | ||
} | ||
/** | ||
* All aliases RHS are required. | ||
*/ | ||
aliasMap[typeAliasResolutionStatus.aliasName] = objectTypeAnnotation; | ||
case '$ReadOnly': | ||
{ | ||
assertGenericTypeAnnotationHasExactlyOneTypeParameter(hasteModuleName, typeAnnotation, parser); | ||
/** | ||
* Nullability of type aliases is transitive. | ||
* | ||
* Consider this case: | ||
* | ||
* type Animal = ?{ | ||
* name: string, | ||
* }; | ||
* | ||
* type B = Animal | ||
* | ||
* export interface Spec extends TurboModule { | ||
* +greet: (animal: B) => void; | ||
* } | ||
* | ||
* In this case, we follow B to Animal, and then Animal to ?{name: string}. | ||
* | ||
* We: | ||
* 1. Replace `+greet: (animal: B) => void;` with `+greet: (animal: ?Animal) => void;`, | ||
* 2. Pretend that Animal = {name: string}. | ||
* | ||
* Why do we do this? | ||
* 1. In ObjC, we need to generate a struct called Animal, not B. | ||
* 2. This design is simpler than managing nullability within both the type alias usage, and the type alias RHS. | ||
* 3. What does it mean for a C++ struct, which is what this type alias RHS will generate, to be nullable? ¯\_(ツ)_/¯ | ||
* Nullability is a concept that only makes sense when talking about instances (i.e: usages) of the C++ structs. | ||
* Hence, it's better to manage nullability within the actual TypeAliasTypeAnnotation nodes, and not the | ||
* associated ObjectTypeAnnotations. | ||
*/ | ||
return wrapNullable(nullable, { | ||
type: 'TypeAliasTypeAnnotation', | ||
name: typeAliasResolutionStatus.aliasName, | ||
}); | ||
} | ||
case 'BooleanTypeAnnotation': { | ||
return wrapNullable(nullable, { | ||
type: 'BooleanTypeAnnotation', | ||
}); | ||
} | ||
case 'NumberTypeAnnotation': { | ||
return wrapNullable(nullable, { | ||
type: 'NumberTypeAnnotation', | ||
}); | ||
} | ||
case 'VoidTypeAnnotation': { | ||
return wrapNullable(nullable, { | ||
type: 'VoidTypeAnnotation', | ||
}); | ||
} | ||
case 'StringTypeAnnotation': { | ||
return wrapNullable(nullable, { | ||
type: 'StringTypeAnnotation', | ||
}); | ||
} | ||
case 'FunctionTypeAnnotation': { | ||
return wrapNullable( | ||
nullable, | ||
translateFunctionTypeAnnotation( | ||
hasteModuleName, | ||
typeAnnotation, | ||
types, | ||
aliasMap, | ||
tryParse, | ||
), | ||
); | ||
} | ||
default: { | ||
throw new UnsupportedFlowTypeAnnotationParserError( | ||
hasteModuleName, | ||
typeAnnotation, | ||
); | ||
} | ||
} | ||
} | ||
const _unwrapNullable3 = unwrapNullable(translateTypeAnnotation(hasteModuleName, typeAnnotation.typeParameters.params[0], types, aliasMap, tryParse, cxxOnly)), | ||
_unwrapNullable4 = _slicedToArray(_unwrapNullable3, 2), | ||
paramType = _unwrapNullable4[0], | ||
isParamNullable = _unwrapNullable4[1]; | ||
function assertGenericTypeAnnotationHasExactlyOneTypeParameter( | ||
moduleName , | ||
/** | ||
* TODO(T71778680): This is a GenericTypeAnnotation. Flow type this node | ||
*/ | ||
typeAnnotation , | ||
) { | ||
if (typeAnnotation.typeParameters == null) { | ||
throw new IncorrectlyParameterizedFlowGenericParserError( | ||
moduleName, | ||
typeAnnotation, | ||
); | ||
} | ||
return wrapNullable(nullable || isParamNullable, paramType); | ||
} | ||
invariant( | ||
typeAnnotation.typeParameters.type === 'TypeParameterInstantiation', | ||
"assertGenericTypeAnnotationHasExactlyOneTypeParameter: Type parameters must be an AST node of type 'TypeParameterInstantiation'", | ||
); | ||
case 'Stringish': | ||
{ | ||
return emitStringish(nullable); | ||
} | ||
if (typeAnnotation.typeParameters.params.length !== 1) { | ||
throw new IncorrectlyParameterizedFlowGenericParserError( | ||
moduleName, | ||
typeAnnotation, | ||
); | ||
} | ||
} | ||
case 'Int32': | ||
{ | ||
return emitInt32(nullable); | ||
} | ||
function translateFunctionTypeAnnotation( | ||
hasteModuleName , | ||
// TODO(T71778680): This is a FunctionTypeAnnotation. Type this. | ||
flowFunctionTypeAnnotation , | ||
types , | ||
aliasMap , | ||
tryParse , | ||
) { | ||
const params = []; | ||
case 'Double': | ||
{ | ||
return emitDouble(nullable); | ||
} | ||
for (const flowParam of (flowFunctionTypeAnnotation.params )) { | ||
const parsedParam = tryParse(() => { | ||
if (flowParam.name == null) { | ||
throw new UnnamedFunctionParamParserError(flowParam, hasteModuleName); | ||
case 'Float': | ||
{ | ||
return emitFloat(nullable); | ||
} | ||
case 'UnsafeObject': | ||
case 'Object': | ||
{ | ||
return emitObject(nullable); | ||
} | ||
default: | ||
{ | ||
return translateDefault(hasteModuleName, typeAnnotation, types, nullable, parser); | ||
} | ||
} | ||
} | ||
const paramName = flowParam.name.name; | ||
const [ | ||
paramTypeAnnotation, | ||
isParamTypeAnnotationNullable, | ||
] = unwrapNullable( | ||
translateTypeAnnotation( | ||
hasteModuleName, | ||
flowParam.typeAnnotation, | ||
types, | ||
aliasMap, | ||
tryParse, | ||
), | ||
); | ||
case 'ObjectTypeAnnotation': | ||
{ | ||
const objectTypeAnnotation = { | ||
type: 'ObjectTypeAnnotation', | ||
// $FlowFixMe[missing-type-arg] | ||
properties: [...typeAnnotation.properties, ...typeAnnotation.indexers].map(property => { | ||
return tryParse(() => { | ||
return parseObjectProperty(property, hasteModuleName, types, aliasMap, tryParse, cxxOnly, nullable, translateTypeAnnotation, parser); | ||
}); | ||
}).filter(Boolean) | ||
}; | ||
return typeAliasResolution(typeAliasResolutionStatus, objectTypeAnnotation, aliasMap, nullable); | ||
} | ||
if (paramTypeAnnotation.type === 'VoidTypeAnnotation') { | ||
throw new UnsupportedFunctionParamTypeAnnotationParserError( | ||
hasteModuleName, | ||
flowParam.typeAnnotation, | ||
paramName, | ||
'void', | ||
); | ||
case 'BooleanTypeAnnotation': | ||
{ | ||
return emitBoolean(nullable); | ||
} | ||
if (paramTypeAnnotation.type === 'PromiseTypeAnnotation') { | ||
throw new UnsupportedFunctionParamTypeAnnotationParserError( | ||
hasteModuleName, | ||
flowParam.typeAnnotation, | ||
paramName, | ||
'Promise', | ||
); | ||
case 'NumberTypeAnnotation': | ||
{ | ||
return emitNumber(nullable); | ||
} | ||
return { | ||
name: flowParam.name.name, | ||
optional: flowParam.optional, | ||
typeAnnotation: wrapNullable( | ||
isParamTypeAnnotationNullable, | ||
paramTypeAnnotation, | ||
), | ||
}; | ||
}); | ||
case 'VoidTypeAnnotation': | ||
{ | ||
return emitVoid(nullable); | ||
} | ||
if (parsedParam != null) { | ||
params.push(parsedParam); | ||
} | ||
} | ||
case 'StringTypeAnnotation': | ||
{ | ||
return emitString(nullable); | ||
} | ||
const [returnTypeAnnotation, isReturnTypeAnnotationNullable] = unwrapNullable( | ||
translateTypeAnnotation( | ||
hasteModuleName, | ||
flowFunctionTypeAnnotation.returnType, | ||
types, | ||
aliasMap, | ||
tryParse, | ||
), | ||
); | ||
case 'FunctionTypeAnnotation': | ||
{ | ||
return emitFunction(nullable, hasteModuleName, typeAnnotation, types, aliasMap, tryParse, cxxOnly, translateTypeAnnotation, language); | ||
} | ||
if (returnTypeAnnotation.type === 'FunctionTypeAnnotation') { | ||
throw new UnsupportedFunctionReturnTypeAnnotationParserError( | ||
hasteModuleName, | ||
flowFunctionTypeAnnotation.returnType, | ||
'FunctionTypeAnnotation', | ||
); | ||
case 'UnionTypeAnnotation': | ||
{ | ||
return emitUnionTypeAnnotation(nullable, hasteModuleName, typeAnnotation, parser); | ||
} | ||
case 'MixedTypeAnnotation': | ||
{ | ||
if (cxxOnly) { | ||
return emitMixedTypeAnnotation(nullable); | ||
} // Fallthrough | ||
} | ||
default: | ||
{ | ||
throw new UnsupportedTypeAnnotationParserError(hasteModuleName, typeAnnotation, language); | ||
} | ||
} | ||
return { | ||
type: 'FunctionTypeAnnotation', | ||
returnTypeAnnotation: wrapNullable( | ||
isReturnTypeAnnotationNullable, | ||
returnTypeAnnotation, | ||
), | ||
params, | ||
}; | ||
} | ||
function buildPropertySchema( | ||
hasteModuleName , | ||
// TODO(T71778680): This is an ObjectTypeProperty containing either: | ||
// - a FunctionTypeAnnotation or GenericTypeAnnotation | ||
// - a NullableTypeAnnoation containing a FunctionTypeAnnotation or GenericTypeAnnotation | ||
// Flow type this node | ||
property , | ||
types , | ||
aliasMap , | ||
tryParse , | ||
) { | ||
function buildPropertySchema(hasteModuleName, // TODO(T71778680): This is an ObjectTypeProperty containing either: | ||
// - a FunctionTypeAnnotation or GenericTypeAnnotation | ||
// - a NullableTypeAnnoation containing a FunctionTypeAnnotation or GenericTypeAnnotation | ||
// Flow type this node | ||
property, types, aliasMap, tryParse, cxxOnly) { | ||
let nullable = false; | ||
let {key, value} = property; | ||
let key = property.key, | ||
value = property.value; | ||
const methodName = key.name; | ||
const methodName = key.name; | ||
var _resolveTypeAnnotatio2 = resolveTypeAnnotation(value, types); | ||
({nullable, typeAnnotation: value} = resolveTypeAnnotation(value, types)); | ||
if (value.type !== 'FunctionTypeAnnotation') { | ||
throw new UnsupportedModulePropertyParserError( | ||
hasteModuleName, | ||
property.value, | ||
property.key.name, | ||
value.type, | ||
); | ||
} | ||
nullable = _resolveTypeAnnotatio2.nullable; | ||
value = _resolveTypeAnnotatio2.typeAnnotation; | ||
throwIfModuleTypeIsUnsupported(hasteModuleName, property.value, property.key.name, value.type, language); | ||
return { | ||
name: methodName, | ||
optional: property.optional, | ||
typeAnnotation: wrapNullable( | ||
nullable, | ||
translateFunctionTypeAnnotation( | ||
hasteModuleName, | ||
value, | ||
types, | ||
aliasMap, | ||
tryParse, | ||
), | ||
), | ||
typeAnnotation: wrapNullable(nullable, translateFunctionTypeAnnotation(hasteModuleName, value, types, aliasMap, tryParse, cxxOnly, translateTypeAnnotation, language)) | ||
}; | ||
@@ -539,46 +289,21 @@ } | ||
function isModuleInterface(node) { | ||
return ( | ||
node.type === 'InterfaceDeclaration' && | ||
node.extends.length === 1 && | ||
node.extends[0].type === 'InterfaceExtends' && | ||
node.extends[0].id.name === 'TurboModule' | ||
); | ||
return node.type === 'InterfaceDeclaration' && node.extends.length === 1 && node.extends[0].type === 'InterfaceExtends' && node.extends[0].id.name === 'TurboModule'; | ||
} | ||
function buildModuleSchema( | ||
hasteModuleName , | ||
/** | ||
* TODO(T71778680): Flow-type this node. | ||
*/ | ||
ast , | ||
tryParse , | ||
) { | ||
function buildModuleSchema(hasteModuleName, | ||
/** | ||
* TODO(T71778680): Flow-type this node. | ||
*/ | ||
ast, tryParse) { | ||
const types = getTypes(ast); | ||
const moduleSpecs = (Object.values(types) ).filter( | ||
isModuleInterface, | ||
); | ||
const moduleSpecs = Object.values(types).filter(isModuleInterface); | ||
throwIfModuleInterfaceNotFound(moduleSpecs.length, hasteModuleName, ast, language); | ||
throwIfMoreThanOneModuleInterfaceParserError(hasteModuleName, moduleSpecs, language); | ||
if (moduleSpecs.length === 0) { | ||
throw new ModuleFlowInterfaceNotFoundParserError(hasteModuleName, ast); | ||
} | ||
const _moduleSpecs = _slicedToArray(moduleSpecs, 1), | ||
moduleSpec = _moduleSpecs[0]; | ||
if (moduleSpecs.length > 1) { | ||
throw new MoreThanOneModuleFlowInterfaceParserError( | ||
hasteModuleName, | ||
moduleSpecs, | ||
moduleSpecs.map(node => node.id.name), | ||
); | ||
} | ||
throwIfModuleInterfaceIsMisnamed(hasteModuleName, moduleSpec.id, language); // Parse Module Names | ||
const [moduleSpec] = moduleSpecs; | ||
if (moduleSpec.id.name !== 'Spec') { | ||
throw new MisnamedModuleFlowInterfaceParserError( | ||
hasteModuleName, | ||
moduleSpec.id, | ||
); | ||
} | ||
// Parse Module Names | ||
const moduleName = tryParse(() => { | ||
const moduleName = tryParse(() => { | ||
const callExpressions = []; | ||
@@ -590,129 +315,60 @@ visit(ast, { | ||
} | ||
}, | ||
} | ||
}); | ||
if (callExpressions.length === 0) { | ||
throw new UnusedModuleFlowInterfaceParserError( | ||
hasteModuleName, | ||
moduleSpec, | ||
); | ||
} | ||
if (callExpressions.length > 1) { | ||
throw new MoreThanOneModuleRegistryCallsParserError( | ||
hasteModuleName, | ||
callExpressions, | ||
callExpressions.length, | ||
); | ||
} | ||
const [callExpression] = callExpressions; | ||
const {typeArguments} = callExpression; | ||
throwIfUnusedModuleInterfaceParserError(hasteModuleName, moduleSpec, callExpressions, language); | ||
throwIfMoreThanOneModuleRegistryCalls(hasteModuleName, callExpressions, callExpressions.length, language); | ||
const callExpression = callExpressions[0]; | ||
const typeArguments = callExpression.typeArguments; | ||
const methodName = callExpression.callee.property.name; | ||
throwIfWrongNumberOfCallExpressionArgs(hasteModuleName, callExpression, methodName, callExpression.arguments.length, language); | ||
if (callExpression.arguments.length !== 1) { | ||
throw new IncorrectModuleRegistryCallArityParserError( | ||
hasteModuleName, | ||
callExpression, | ||
methodName, | ||
callExpression.arguments.length, | ||
); | ||
} | ||
if (callExpression.arguments[0].type !== 'Literal') { | ||
const {type} = callExpression.arguments[0]; | ||
throw new IncorrectModuleRegistryCallArgumentTypeParserError( | ||
hasteModuleName, | ||
callExpression.arguments[0], | ||
methodName, | ||
type, | ||
); | ||
const type = callExpression.arguments[0].type; | ||
throw new IncorrectModuleRegistryCallArgumentTypeParserError(hasteModuleName, callExpression.arguments[0], methodName, type, language); | ||
} | ||
const $moduleName = callExpression.arguments[0].value; | ||
if (typeArguments == null) { | ||
throw new UntypedModuleRegistryCallParserError( | ||
hasteModuleName, | ||
callExpression, | ||
methodName, | ||
$moduleName, | ||
); | ||
} | ||
if ( | ||
typeArguments.type !== 'TypeParameterInstantiation' || | ||
typeArguments.params.length !== 1 || | ||
typeArguments.params[0].type !== 'GenericTypeAnnotation' || | ||
typeArguments.params[0].id.name !== 'Spec' | ||
) { | ||
throw new IncorrectModuleRegistryCallTypeParameterParserError( | ||
hasteModuleName, | ||
typeArguments, | ||
methodName, | ||
$moduleName, | ||
); | ||
} | ||
throwIfUntypedModule(typeArguments, hasteModuleName, callExpression, methodName, $moduleName, language); | ||
throwIfIncorrectModuleRegistryCallTypeParameterParserError(hasteModuleName, typeArguments, methodName, $moduleName, parser); | ||
return $moduleName; | ||
}); | ||
const moduleNames = moduleName == null ? [] : [moduleName]; | ||
// Some module names use platform suffix to indicate platform-exclusive modules. | ||
const moduleNames = moduleName == null ? [] : [moduleName]; // Some module names use platform suffix to indicate platform-exclusive modules. | ||
// Eventually this should be made explicit in the Flow type itself. | ||
// Also check the hasteModuleName for platform suffix. | ||
// Note: this shape is consistent with ComponentSchema. | ||
const excludedPlatforms = []; | ||
const namesToValidate = [...moduleNames, hasteModuleName]; | ||
namesToValidate.forEach(name => { | ||
if (name.endsWith('Android')) { | ||
excludedPlatforms.push('iOS'); | ||
} else if (name.endsWith('IOS')) { | ||
excludedPlatforms.push('android'); | ||
} | ||
}); | ||
// $FlowFixMe[missing-type-arg] | ||
return (moduleSpec.body.properties ) | ||
.filter(property => property.type === 'ObjectTypeProperty') | ||
.map | ||
(property => { | ||
const aliasMap = {}; | ||
const _verifyPlatforms = verifyPlatforms(hasteModuleName, moduleNames), | ||
cxxOnly = _verifyPlatforms.cxxOnly, | ||
excludedPlatforms = _verifyPlatforms.excludedPlatforms; // $FlowFixMe[missing-type-arg] | ||
return tryParse(() => ({ | ||
aliasMap: aliasMap, | ||
propertyShape: buildPropertySchema( | ||
hasteModuleName, | ||
property, | ||
types, | ||
aliasMap, | ||
tryParse, | ||
), | ||
})); | ||
}) | ||
.filter(Boolean) | ||
.reduce( | ||
(moduleSchema , {aliasMap, propertyShape}) => { | ||
return { | ||
type: 'NativeModule', | ||
aliases: {...moduleSchema.aliases, ...aliasMap}, | ||
spec: { | ||
properties: [...moduleSchema.spec.properties, propertyShape], | ||
}, | ||
moduleNames: moduleSchema.moduleNames, | ||
excludedPlatforms: moduleSchema.excludedPlatforms, | ||
}; | ||
return moduleSpec.body.properties.filter(property => property.type === 'ObjectTypeProperty').map(property => { | ||
const aliasMap = {}; | ||
return tryParse(() => ({ | ||
aliasMap: aliasMap, | ||
propertyShape: buildPropertySchema(hasteModuleName, property, types, aliasMap, tryParse, cxxOnly) | ||
})); | ||
}).filter(Boolean).reduce((moduleSchema, { | ||
aliasMap, | ||
propertyShape | ||
}) => { | ||
return { | ||
type: 'NativeModule', | ||
aliases: _objectSpread({}, moduleSchema.aliases, {}, aliasMap), | ||
spec: { | ||
properties: [...moduleSchema.spec.properties, propertyShape] | ||
}, | ||
{ | ||
type: 'NativeModule', | ||
aliases: {}, | ||
spec: {properties: []}, | ||
moduleNames: moduleNames, | ||
excludedPlatforms: | ||
excludedPlatforms.length !== 0 ? [...excludedPlatforms] : undefined, | ||
}, | ||
); | ||
moduleNames: moduleSchema.moduleNames, | ||
excludedPlatforms: moduleSchema.excludedPlatforms | ||
}; | ||
}, { | ||
type: 'NativeModule', | ||
aliases: {}, | ||
spec: { | ||
properties: [] | ||
}, | ||
moduleNames: moduleNames, | ||
excludedPlatforms: excludedPlatforms.length !== 0 ? [...excludedPlatforms] : undefined | ||
}); | ||
} | ||
@@ -722,2 +378,3 @@ | ||
buildModuleSchema, | ||
}; | ||
flowTranslateTypeAnnotation: translateTypeAnnotation | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,7 +10,3 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
const {ParserError} = require('./errors'); | ||
/** | ||
@@ -24,55 +20,29 @@ * This FlowFixMe is supposed to refer to an InterfaceDeclaration or TypeAlias | ||
*/ | ||
function getTypes(ast ) { | ||
function getTypes(ast) { | ||
return ast.body.reduce((types, node) => { | ||
if (node.type === 'ExportNamedDeclaration' && node.exportKind === 'type') { | ||
if ( | ||
node.declaration.type === 'TypeAlias' || | ||
node.declaration.type === 'InterfaceDeclaration' | ||
) { | ||
if (node.declaration != null && (node.declaration.type === 'TypeAlias' || node.declaration.type === 'InterfaceDeclaration')) { | ||
types[node.declaration.id.name] = node.declaration; | ||
} | ||
} else if ( | ||
node.type === 'TypeAlias' || | ||
node.type === 'InterfaceDeclaration' | ||
) { | ||
} 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; | ||
}, {}); | ||
} | ||
} // $FlowFixMe[unclear-type] there's no flowtype for ASTs | ||
// $FlowFixMe[unclear-type] there's no flowtype for ASTs | ||
const invariant = require('invariant'); | ||
function resolveTypeAnnotation( | ||
// TODO(T71778680): This is an Flow TypeAnnotation. Flow-type this | ||
typeAnnotation , | ||
types , | ||
) | ||
{ | ||
invariant( | ||
typeAnnotation != null, | ||
'resolveTypeAnnotation(): typeAnnotation cannot be null', | ||
); | ||
function resolveTypeAnnotation( // TODO(T71778680): This is an Flow TypeAnnotation. Flow-type this | ||
typeAnnotation, types) { | ||
invariant(typeAnnotation != null, 'resolveTypeAnnotation(): typeAnnotation cannot be null'); | ||
let node = typeAnnotation; | ||
let nullable = false; | ||
let typeAliasResolutionStatus = { | ||
successful: false, | ||
let typeAliasResolutionStatus = { | ||
successful: false | ||
}; | ||
@@ -87,14 +57,11 @@ | ||
successful: true, | ||
aliasName: node.id.name, | ||
aliasName: node.id.name | ||
}; | ||
const resolvedTypeAnnotation = types[node.id.name]; | ||
if (resolvedTypeAnnotation == null) { | ||
if (resolvedTypeAnnotation == null || resolvedTypeAnnotation.type === 'EnumDeclaration') { | ||
break; | ||
} | ||
invariant( | ||
resolvedTypeAnnotation.type === 'TypeAlias', | ||
`GenericTypeAnnotation '${node.id.name}' must resolve to a TypeAlias. Instead, it resolved to a '${resolvedTypeAnnotation.type}'`, | ||
); | ||
invariant(resolvedTypeAnnotation.type === 'TypeAlias', `GenericTypeAnnotation '${node.id.name}' must resolve to a TypeAlias. Instead, it resolved to a '${resolvedTypeAnnotation.type}'`); | ||
node = resolvedTypeAnnotation.right; | ||
@@ -109,106 +76,18 @@ } else { | ||
typeAnnotation: node, | ||
typeAliasResolutionStatus, | ||
typeAliasResolutionStatus | ||
}; | ||
} | ||
function getValueFromTypes(value , types ) { | ||
function getValueFromTypes(value, types) { | ||
if (value.type === 'GenericTypeAnnotation' && types[value.id.name]) { | ||
return getValueFromTypes(types[value.id.name].right, types); | ||
} | ||
return value; | ||
} | ||
function createParserErrorCapturer() | ||
{ | ||
const errors = []; | ||
function guard (fn ) { | ||
try { | ||
return fn(); | ||
} catch (error) { | ||
if (!(error instanceof ParserError)) { | ||
throw error; | ||
} | ||
errors.push(error); | ||
return null; | ||
} | ||
} | ||
return [errors, guard]; | ||
} | ||
// TODO(T71778680): Flow-type ASTNodes. | ||
function visit( | ||
astNode , | ||
visitor | ||
, | ||
) { | ||
const queue = [astNode]; | ||
while (queue.length !== 0) { | ||
let item = queue.shift(); | ||
if (!(typeof item === 'object' && item != null)) { | ||
continue; | ||
} | ||
if ( | ||
typeof item.type === 'string' && | ||
typeof visitor[item.type] === 'function' | ||
) { | ||
// Don't visit any children | ||
visitor[item.type](item); | ||
} else if (Array.isArray(item)) { | ||
queue.push(...item); | ||
} else { | ||
queue.push(...Object.values(item)); | ||
} | ||
} | ||
} | ||
// TODO(T71778680): Flow-type ASTNodes. | ||
function isModuleRegistryCall(node ) { | ||
if (node.type !== 'CallExpression') { | ||
return false; | ||
} | ||
const callExpression = node; | ||
if (callExpression.callee.type !== 'MemberExpression') { | ||
return false; | ||
} | ||
const memberExpression = callExpression.callee; | ||
if ( | ||
!( | ||
memberExpression.object.type === 'Identifier' && | ||
memberExpression.object.name === 'TurboModuleRegistry' | ||
) | ||
) { | ||
return false; | ||
} | ||
if ( | ||
!( | ||
memberExpression.property.type === 'Identifier' && | ||
(memberExpression.property.name === 'get' || | ||
memberExpression.property.name === 'getEnforcing') | ||
) | ||
) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
module.exports = { | ||
getValueFromTypes, | ||
resolveTypeAnnotation, | ||
createParserErrorCapturer, | ||
getTypes, | ||
visit, | ||
isModuleRegistryCall, | ||
}; | ||
getTypes | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,13 +10,9 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
function parse(filename ) { | ||
function parse(filename) { | ||
try { | ||
// $FlowFixMe[unsupported-syntax] Can't require dynamic variables | ||
return require(filename); | ||
} catch (err) { | ||
// Ignore | ||
} catch (err) {// Ignore | ||
} | ||
@@ -26,3 +22,3 @@ } | ||
module.exports = { | ||
parse, | ||
}; | ||
parse | ||
}; |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
@@ -10,3 +10,2 @@ * This source code is licensed under the MIT license found in the | ||
*/ | ||
'use strict'; | ||
@@ -16,10 +15,6 @@ | ||
function getErrors(schema) { | ||
const errors = new Set(); // Map of component name -> Array of module names | ||
function getErrors(schema ) { | ||
const errors = new Set(); | ||
// Map of component name -> Array of module names | ||
const componentModules = new Map(); | ||
const componentModules = new Map(); | ||
Object.keys(schema.modules).forEach(moduleName => { | ||
@@ -44,17 +39,11 @@ const module = schema.modules[moduleName]; | ||
}); | ||
componentModules.forEach((modules, componentName) => { | ||
if (modules.length > 1) { | ||
errors.add( | ||
`Duplicate components found with name ${componentName}. Found in modules ${modules.join( | ||
', ', | ||
)}`, | ||
); | ||
errors.add(`Duplicate components found with name ${componentName}. Found in modules ${modules.join(', ')}`); | ||
} | ||
}); | ||
return Array.from(errors).sort(); | ||
} | ||
function validate(schema ) { | ||
function validate(schema) { | ||
const errors = getErrors(schema); | ||
@@ -69,3 +58,3 @@ | ||
getErrors, | ||
validate, | ||
}; | ||
validate | ||
}; |
{ | ||
"name": "react-native-tscodegen", | ||
"version": "0.68.5", | ||
"version": "0.71.0", | ||
"description": "TypeScript Code Generation for React Native Turbo Module", | ||
"main": "./lib/index.js", | ||
"bin": "./bin/cli", | ||
"typings": "./lib/index.d.ts", | ||
"scripts": { | ||
"build": "flow-remove-types ../../react-native/packages/react-native-codegen/ -q -d lib/rncodegen/ & tslint --project tsconfig.json & tsc" | ||
"clean-flow": "rimraf lib/rncodegen", | ||
"build-flow": "flow-remove-types ../../react-native/packages/react-native-codegen/ -q -d lib/rncodegen/", | ||
"fix-flow": "babel lib/rncodegen/ -d lib/rncodegen", | ||
"patch-rncodegen": "node ./patch-rncodegen.js", | ||
"build-ts": "tslint --project tsconfig.json & tsc", | ||
"build": "npm run clean-flow & npm run build-flow & npm run fix-flow & npm run patch-rncodegen & npm run build-ts" | ||
}, | ||
@@ -25,9 +29,30 @@ "repository": { | ||
"tslint-microsoft-contrib": "^6.2.0", | ||
"@react-native-tscodegen/tslint-shared": "^1.0.0" | ||
"rimraf": "3.0.2", | ||
"@react-native-tscodegen/tslint-shared": "^1.0.0", | ||
"@babel/cli": "7.18.10", | ||
"@babel/plugin-proposal-class-properties": "^7.0.0", | ||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0", | ||
"@babel/plugin-proposal-object-rest-spread": "^7.0.0", | ||
"@babel/plugin-proposal-optional-chaining": "^7.0.0", | ||
"@babel/plugin-syntax-dynamic-import": "^7.0.0", | ||
"@babel/plugin-transform-async-to-generator": "^7.0.0", | ||
"@babel/plugin-transform-destructuring": "^7.0.0", | ||
"@babel/plugin-transform-flow-strip-types": "^7.0.0" | ||
}, | ||
"dependencies": { | ||
"typescript": "^4.1.5", | ||
"jscodeshift": "0.6.4", | ||
"nullthrows": "1.1.1" | ||
}, | ||
"babel": { | ||
"plugins": [ | ||
"@babel/plugin-proposal-object-rest-spread", | ||
"@babel/plugin-transform-async-to-generator", | ||
"@babel/plugin-transform-destructuring", | ||
"@babel/plugin-transform-flow-strip-types", | ||
"@babel/plugin-syntax-dynamic-import", | ||
"@babel/plugin-proposal-class-properties", | ||
"@babel/plugin-proposal-nullish-coalescing-operator", | ||
"@babel/plugin-proposal-optional-chaining" | ||
] | ||
} | ||
} |
@@ -7,3 +7,3 @@ # Welcome to react-native-tscodegen (beta)! | ||
0.65-stable | ||
0.70.1 | ||
@@ -47,4 +47,4 @@ ## Authoring a Turbo Module | ||
"shadow-nodes", | ||
"modulesAndroid" | ||
"modulesCxx" | ||
"modulesAndroid", | ||
"modulesCxx", | ||
"modulesIOS" | ||
@@ -51,0 +51,0 @@ ], |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
646358
2
102
15983
15
6
1
- Removedtypescript@^4.1.5
- Removedtypescript@4.9.5(transitive)