react-native-tscodegen
Advanced tools
Comparing version 0.66.1 to 0.67.0
@@ -5,3 +5,17 @@ { | ||
{ | ||
"date": "Wed, 12 Aug 2020 22:27:19 GMT", | ||
"date": "Sat, 27 Feb 2021 02:56:20 GMT", | ||
"tag": "react-native-tscodegen_v0.67.0", | ||
"version": "0.67.0", | ||
"comments": { | ||
"minor": [ | ||
{ | ||
"comment": "catch up to react-native 0.64", | ||
"author": "zihanc@microsoft.com", | ||
"commit": "a15efdc739c0941922c5f9807eefa2541bf026ee" | ||
} | ||
] | ||
} | ||
}, | ||
{ | ||
"date": "Wed, 12 Aug 2020 22:27:25 GMT", | ||
"tag": "react-native-tscodegen_v0.66.1", | ||
@@ -8,0 +22,0 @@ "version": "0.66.1", |
# Change Log - react-native-tscodegen | ||
This log was last generated on Wed, 12 Aug 2020 22:27:19 GMT and should not be manually modified. | ||
This log was last generated on Sat, 27 Feb 2021 02:56:20 GMT and should not be manually modified. | ||
## 0.67.0 | ||
Sat, 27 Feb 2021 02:56:20 GMT | ||
### Minor changes | ||
- catch up to react-native 0.64 (zihanc@microsoft.com) | ||
## 0.66.1 | ||
Wed, 12 Aug 2020 22:27:19 GMT | ||
Wed, 12 Aug 2020 22:27:25 GMT | ||
@@ -8,0 +14,0 @@ ### Patches |
@@ -20,3 +20,5 @@ "use strict"; | ||
'shadow-nodes', | ||
'modules' | ||
'modulesAndroid', | ||
'modulesCxx', | ||
'modulesIOS' | ||
]; | ||
@@ -23,0 +25,0 @@ var config = JSON.parse(fs_1.readFileSync(process.argv[2], { encoding: 'utf-8' })); |
@@ -0,1 +1,2 @@ | ||
export declare type PlatformType = 'iOS' | 'android'; | ||
export declare type CommandsFunctionTypeAnnotation = Readonly<{ | ||
@@ -9,3 +10,7 @@ type: 'FunctionTypeAnnotation'; | ||
}>; | ||
export declare type CommandsTypeAnnotation = BooleanTypeAnnotation | Int32TypeAnnotation | DoubleTypeAnnotation | FloatTypeAnnotation | StringTypeAnnotation; | ||
export declare type CommandsTypeAnnotation = ReservedFunctionValueTypeAnnotation | BooleanTypeAnnotation | Int32TypeAnnotation | DoubleTypeAnnotation | FloatTypeAnnotation | StringTypeAnnotation; | ||
export declare type ReservedFunctionValueTypeAnnotation = Readonly<{ | ||
type: 'ReservedFunctionValueTypeAnnotation'; | ||
name: ReservedFunctionValueTypeName; | ||
}>; | ||
export declare type DoubleTypeAnnotation = Readonly<{ | ||
@@ -26,3 +31,3 @@ type: 'DoubleTypeAnnotation'; | ||
}>; | ||
export declare type ObjectPropertyType = Readonly<{ | ||
export declare type EventObjectPropertyType = Readonly<{ | ||
type: 'BooleanTypeAnnotation'; | ||
@@ -58,3 +63,3 @@ name: string; | ||
optional: boolean; | ||
properties: ReadonlyArray<ObjectPropertyType>; | ||
properties: ReadonlyArray<EventObjectPropertyType>; | ||
}>; | ||
@@ -131,55 +136,2 @@ export declare type PropTypeTypeAnnotation = Readonly<{ | ||
}>; | ||
export declare type PrimitiveTypeAnnotationType = 'StringTypeAnnotation' | 'NumberTypeAnnotation' | 'Int32TypeAnnotation' | 'DoubleTypeAnnotation' | 'FloatTypeAnnotation' | 'BooleanTypeAnnotation' | 'GenericObjectTypeAnnotation'; | ||
export declare type PrimitiveTypeAnnotation = Readonly<{ | ||
type: PrimitiveTypeAnnotationType; | ||
}>; | ||
export declare type ReservedFunctionValueTypeName = 'RootTag'; | ||
export declare type FunctionTypeAnnotationParamTypeAnnotation = Readonly<{ | ||
type: 'AnyTypeAnnotation' | 'FunctionTypeAnnotation' | PrimitiveTypeAnnotationType; | ||
}> | Readonly<{ | ||
type: 'ArrayTypeAnnotation'; | ||
elementType: (undefined | FunctionTypeAnnotationParamTypeAnnotation); | ||
}> | Readonly<{ | ||
type: 'ObjectTypeAnnotation'; | ||
properties: (undefined | ReadonlyArray<ObjectParamTypeAnnotation>); | ||
}> | Readonly<{ | ||
type: 'ReservedFunctionValueTypeAnnotation'; | ||
name: ReservedFunctionValueTypeName; | ||
}>; | ||
export declare type FunctionTypeAnnotationReturnArrayElementType = FunctionTypeAnnotationParamTypeAnnotation; | ||
export declare type ObjectParamTypeAnnotation = Readonly<{ | ||
optional: boolean; | ||
name: string; | ||
typeAnnotation: FunctionTypeAnnotationParamTypeAnnotation; | ||
}>; | ||
export declare type FunctionTypeAnnotationReturn = Readonly<{ | ||
nullable: boolean; | ||
type: PrimitiveTypeAnnotationType | 'VoidTypeAnnotation' | 'GenericPromiseTypeAnnotation'; | ||
}> | Readonly<{ | ||
nullable: boolean; | ||
type: 'ArrayTypeAnnotation'; | ||
elementType: (undefined | FunctionTypeAnnotationReturnArrayElementType); | ||
}> | Readonly<{ | ||
nullable: boolean; | ||
type: 'ObjectTypeAnnotation'; | ||
properties: (undefined | ReadonlyArray<ObjectParamTypeAnnotation>); | ||
}>; | ||
export declare type FunctionTypeAnnotationParam = Readonly<{ | ||
nullable: boolean; | ||
name: string; | ||
typeAnnotation: FunctionTypeAnnotationParamTypeAnnotation; | ||
}>; | ||
export declare type FunctionTypeAnnotation = Readonly<{ | ||
type: 'FunctionTypeAnnotation'; | ||
params: ReadonlyArray<FunctionTypeAnnotationParam>; | ||
returnTypeAnnotation: FunctionTypeAnnotationReturn; | ||
optional: boolean; | ||
}>; | ||
export declare type MethodTypeShape = Readonly<{ | ||
name: string; | ||
typeAnnotation: FunctionTypeAnnotation; | ||
}>; | ||
export declare type NativeModuleShape = Readonly<{ | ||
properties: ReadonlyArray<MethodTypeShape>; | ||
}>; | ||
export declare type EventTypeShape = Readonly<{ | ||
@@ -194,3 +146,3 @@ name: string; | ||
type: 'ObjectTypeAnnotation'; | ||
properties: ReadonlyArray<ObjectPropertyType>; | ||
properties: ReadonlyArray<EventObjectPropertyType>; | ||
}>; | ||
@@ -207,3 +159,3 @@ }>; | ||
paperComponentName?: string; | ||
excludedPlatform?: 'iOS' | 'android'; | ||
excludedPlatforms?: ReadonlyArray<PlatformType>; | ||
paperComponentNameDeprecated?: string; | ||
@@ -223,11 +175,98 @@ }>; | ||
modules: Readonly<{ | ||
[module: string]: Readonly<{ | ||
components?: Readonly<{ | ||
[component: string]: ComponentShape; | ||
}>; | ||
nativeModules?: Readonly<{ | ||
[nativeModule: string]: NativeModuleShape; | ||
}>; | ||
}>; | ||
[hasteModuleName: string]: ComponentSchema | NativeModuleSchema; | ||
}>; | ||
}>; | ||
export declare type ComponentSchema = Readonly<{ | ||
type: 'Component'; | ||
components: Readonly<{ | ||
[componentName: string]: ComponentShape; | ||
}>; | ||
}>; | ||
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<NativeModulePropertySchema>; | ||
}>; | ||
export declare type NativeModulePropertySchema = Readonly<{ | ||
name: string; | ||
optional: boolean; | ||
typeAnnotation: Nullable<NativeModuleFunctionTypeAnnotation>; | ||
}>; | ||
export declare type NativeModuleAliasMap = Readonly<{ | ||
[aliasName: string]: NativeModuleObjectTypeAnnotation; | ||
}>; | ||
export declare type NativeModuleFunctionTypeAnnotation = Readonly<{ | ||
type: 'FunctionTypeAnnotation'; | ||
params: ReadonlyArray<NativeModuleMethodParamSchema>; | ||
returnTypeAnnotation: Nullable<NativeModuleReturnTypeAnnotation>; | ||
}>; | ||
export declare type NativeModuleMethodParamSchema = Readonly<{ | ||
name: string; | ||
optional: boolean; | ||
typeAnnotation: Nullable<NativeModuleParamTypeAnnotation>; | ||
}>; | ||
export declare type NativeModuleObjectTypeAnnotation = Readonly<{ | ||
type: 'ObjectTypeAnnotation'; | ||
properties: ReadonlyArray<NativeModuleObjectTypeAnnotationPropertySchema>; | ||
}>; | ||
export declare type NativeModuleObjectTypeAnnotationPropertySchema = Readonly<{ | ||
name: string; | ||
optional: boolean; | ||
typeAnnotation: Nullable<NativeModuleBaseTypeAnnotation>; | ||
}>; | ||
export declare type NativeModuleArrayTypeAnnotation = Readonly<{ | ||
type: 'ArrayTypeAnnotation'; | ||
elementType?: Nullable<NativeModuleBaseTypeAnnotation>; | ||
}>; | ||
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 NativeModuleReservedFunctionValueTypeAnnotation = Readonly<{ | ||
type: 'ReservedFunctionValueTypeAnnotation'; | ||
name: ReservedFunctionValueTypeName; | ||
}>; | ||
export declare type NativeModuleTypeAliasTypeAnnotation = Readonly<{ | ||
type: 'TypeAliasTypeAnnotation'; | ||
name: string; | ||
}>; | ||
export declare type NativeModulePromiseTypeAnnotation = Readonly<{ | ||
type: 'PromiseTypeAnnotation'; | ||
}>; | ||
export declare type NativeModuleVoidTypeAnnotation = Readonly<{ | ||
type: 'VoidTypeAnnotation'; | ||
}>; | ||
export declare type NativeModuleBaseTypeAnnotation = NativeModuleStringTypeAnnotation | NativeModuleNumberTypeAnnotation | NativeModuleInt32TypeAnnotation | NativeModuleDoubleTypeAnnotation | NativeModuleFloatTypeAnnotation | NativeModuleBooleanTypeAnnotation | NativeModuleGenericObjectTypeAnnotation | NativeModuleReservedFunctionValueTypeAnnotation | NativeModuleTypeAliasTypeAnnotation | NativeModuleArrayTypeAnnotation | NativeModuleObjectTypeAnnotation; | ||
export declare type NativeModuleParamTypeAnnotation = NativeModuleBaseTypeAnnotation | NativeModuleParamOnlyTypeAnnotation; | ||
export declare type NativeModuleReturnTypeAnnotation = NativeModuleBaseTypeAnnotation | NativeModuleReturnOnlyTypeAnnotation; | ||
export declare type NativeModuleTypeAnnotation = NativeModuleBaseTypeAnnotation | NativeModuleParamOnlyTypeAnnotation | NativeModuleReturnOnlyTypeAnnotation; | ||
export declare type NativeModuleParamOnlyTypeAnnotation = NativeModuleFunctionTypeAnnotation; | ||
export declare type NativeModuleReturnOnlyTypeAnnotation = NativeModulePromiseTypeAnnotation | NativeModuleVoidTypeAnnotation; | ||
export declare type ReservedFunctionValueTypeName = 'RootTag'; |
import * as cs from './CodegenSchema'; | ||
import { ExportCommandInfo } from './ExportParser'; | ||
export declare function parseCommands(info: ExportCommandInfo): cs.CommandTypeShape[]; | ||
export declare function parseCommands(info?: ExportCommandInfo): cs.CommandTypeShape[]; |
@@ -5,2 +5,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.parseCommands = void 0; | ||
var ts = require("typescript"); | ||
@@ -10,3 +11,3 @@ var ExportParser_1 = require("./ExportParser"); | ||
function typeNodeToCommandsTypeAnnotation(typeNode, sourceFile) { | ||
var rawType = TypeChecker_1.typeToRNRawType(typeNode, sourceFile, false); | ||
var rawType = TypeChecker_1.typeToRNRawType(typeNode, sourceFile, { allowObject: false }); | ||
switch (rawType.kind) { | ||
@@ -18,2 +19,3 @@ case 'String': return { type: 'StringTypeAnnotation' }; | ||
case 'Boolean': return { type: 'BooleanTypeAnnotation' }; | ||
case 'rn:RootTag': return { type: 'ReservedFunctionValueTypeAnnotation', name: 'RootTag' }; | ||
default: | ||
@@ -24,2 +26,5 @@ } | ||
function parseCommands(info) { | ||
if (info === undefined) { | ||
return []; | ||
} | ||
var validMembers = ExportParser_1.getMembersFromType(info.typeNode, info.sourceFile); | ||
@@ -26,0 +31,0 @@ if (validMembers === undefined) { |
import * as ts from 'typescript'; | ||
import * as cs from './CodegenSchema'; | ||
import { ExportComponentInfo } from './ExportParser'; | ||
export declare function tryParseEvent(info: ExportComponentInfo, propDecl: ts.PropertySignature | ts.PropertyDeclaration): cs.EventTypeShape | undefined; | ||
export interface ComponentEventInfo { | ||
optional: boolean; | ||
eventType: ts.TypeNode; | ||
eventTypeName: string; | ||
paperTopLevelNameDeprecated?: string; | ||
} | ||
export declare function checkEventType(eventType: ts.TypeNode, info: ExportComponentInfo, propDecl: ts.PropertySignature | ts.PropertyDeclaration): ComponentEventInfo | undefined; | ||
export declare function parseEvent(info: ExportComponentInfo, propDecl: ts.PropertySignature | ts.PropertyDeclaration, eventInfo: ComponentEventInfo): cs.EventTypeShape; |
@@ -5,2 +5,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.parseEvent = exports.checkEventType = void 0; | ||
var ts = require("typescript"); | ||
@@ -27,3 +28,3 @@ var TypeChecker_1 = require("./TypeChecker"); | ||
if (result !== undefined) { | ||
result[0] = true; | ||
result.optional = true; | ||
} | ||
@@ -44,6 +45,15 @@ return result; | ||
if (ts.isLiteralTypeNode(nameArgument) && ts.isStringLiteral(nameArgument.literal)) { | ||
return [false, typeArguments[0], typeName, nameArgument.literal.text]; | ||
return { | ||
optional: false, | ||
eventType: typeArguments[0], | ||
eventTypeName: typeName, | ||
paperTopLevelNameDeprecated: nameArgument.literal.text | ||
}; | ||
} | ||
} | ||
return [false, typeArguments[0], typeName, undefined]; | ||
return { | ||
optional: false, | ||
eventType: typeArguments[0], | ||
eventTypeName: typeName | ||
}; | ||
} | ||
@@ -54,2 +64,3 @@ else { | ||
} | ||
exports.checkEventType = checkEventType; | ||
function rnRawTypeToObjectPropertyType(typeNode, rawType) { | ||
@@ -76,5 +87,14 @@ var namePlaceholder = undefined; | ||
prop.name = rawProp.name; | ||
if (rawProp.optional) { | ||
prop.optional = true; | ||
} | ||
return prop; | ||
}) | ||
}; | ||
case 'Null': return { | ||
type: 'ObjectTypeAnnotation', | ||
name: namePlaceholder, | ||
optional: rawType.isNullable, | ||
properties: [] | ||
}; | ||
default: | ||
@@ -84,10 +104,5 @@ } | ||
} | ||
function tryParseEvent(info, propDecl) { | ||
function parseEvent(info, propDecl, eventInfo) { | ||
var propType = propDecl.type; | ||
var eventTypeTuple = checkEventType(propType, info, propDecl); | ||
if (eventTypeTuple === undefined) { | ||
return undefined; | ||
} | ||
var optional = eventTypeTuple[0], eventType = eventTypeTuple[1], eventTypeName = eventTypeTuple[2], paperTopLevelNameDeprecated = eventTypeTuple[3]; | ||
var rawType = TypeChecker_1.typeToRNRawType(eventType, info.sourceFile, true); | ||
var rawType = TypeChecker_1.typeToRNRawType(eventInfo.eventType, info.sourceFile, { allowObject: true }); | ||
var eventProperties = []; | ||
@@ -105,4 +120,4 @@ if (rawType.kind !== 'Null') { | ||
name: propDecl.name.getText(), | ||
bubblingType: eventTypeName === 'DirectEventHandler' ? 'direct' : 'bubble', | ||
optional: (propDecl.questionToken !== undefined) || optional, | ||
bubblingType: eventInfo.eventTypeName === 'DirectEventHandler' ? 'direct' : 'bubble', | ||
optional: (propDecl.questionToken !== undefined) || eventInfo.optional, | ||
typeAnnotation: { | ||
@@ -116,8 +131,8 @@ type: 'EventTypeAnnotation', | ||
}; | ||
if (paperTopLevelNameDeprecated !== undefined) { | ||
result.paperTopLevelNameDeprecated = paperTopLevelNameDeprecated; | ||
if (eventInfo.paperTopLevelNameDeprecated !== undefined) { | ||
result.paperTopLevelNameDeprecated = eventInfo.paperTopLevelNameDeprecated; | ||
} | ||
return result; | ||
} | ||
exports.tryParseEvent = tryParseEvent; | ||
exports.parseEvent = parseEvent; | ||
//# sourceMappingURL=ComponentEventParser.js.map |
import * as cs from './CodegenSchema'; | ||
import { ExportCommandInfo, ExportComponentInfo } from './ExportParser'; | ||
export declare function processComponent(info: ExportComponentInfo, commandsInfo: ExportCommandInfo | undefined): cs.ComponentShape; | ||
export declare function processComponent(info: ExportComponentInfo, commandsInfo?: ExportCommandInfo): cs.ComponentSchema; |
@@ -5,2 +5,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.processComponent = void 0; | ||
var ts = require("typescript"); | ||
@@ -35,8 +36,8 @@ var ComponentCommandParser_1 = require("./ComponentCommandParser"); | ||
function processComponent(info, commandsInfo) { | ||
var _a; | ||
var extendsProps = []; | ||
var events = []; | ||
var props = []; | ||
var commands = []; | ||
if (commandsInfo !== undefined) { | ||
commands = ComponentCommandParser_1.parseCommands(commandsInfo); | ||
} | ||
commands = ComponentCommandParser_1.parseCommands(commandsInfo); | ||
var validMembers = ExportParser_1.getMembersFromType(info.typeNode, info.sourceFile); | ||
@@ -51,18 +52,8 @@ if (validMembers === undefined) { | ||
if (ts.isPropertySignature(propDecl) || ts.isPropertyDeclaration(propDecl)) { | ||
try { | ||
var eventShape = ComponentEventParser_1.tryParseEvent(info, propDecl); | ||
if (eventShape !== undefined) { | ||
events.push(eventShape); | ||
} | ||
else { | ||
props.push(ComponentPropertyParser_1.parseProperty(info, propDecl)); | ||
} | ||
var eventInfo = ComponentEventParser_1.checkEventType(propDecl.type, info, propDecl); | ||
if (eventInfo === undefined) { | ||
props.push(ComponentPropertyParser_1.parseProperty(info, propDecl)); | ||
} | ||
catch (err) { | ||
if (err instanceof Error) { | ||
err.message = propertyName + ": " + err.message; | ||
} | ||
else { | ||
throw err; | ||
} | ||
else { | ||
events.push(ComponentEventParser_1.parseEvent(info, propDecl, eventInfo)); | ||
} | ||
@@ -75,4 +66,7 @@ } | ||
} | ||
var result = { | ||
extendsProps: [], | ||
if (importExists(info.sourceFile, 'ViewProps')) { | ||
extendsProps.push({ knownTypeName: 'ReactNativeCoreViewProps', type: 'ReactNativeBuiltInType' }); | ||
} | ||
var shape = { | ||
extendsProps: extendsProps, | ||
events: events, | ||
@@ -82,11 +76,8 @@ props: props, | ||
}; | ||
if (importExists(info.sourceFile, 'ViewProps')) { | ||
result.extendsProps.push({ knownTypeName: 'ReactNativeCoreViewProps', type: 'ReactNativeBuiltInType' }); | ||
} | ||
Object.getOwnPropertyNames(info.options).forEach(function (key) { | ||
result[key] = info.options[key]; | ||
shape[key] = info.options[key]; | ||
}); | ||
return result; | ||
return { type: 'Component', components: (_a = {}, _a[info.name] = shape, _a) }; | ||
} | ||
exports.processComponent = processComponent; | ||
//# sourceMappingURL=ComponentParser.js.map |
@@ -5,2 +5,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.parseProperty = void 0; | ||
var TypeChecker_1 = require("./TypeChecker"); | ||
@@ -14,3 +15,3 @@ function rnRawTypeToObjectTypeAnnotation(rawType, typeNode) { | ||
name: value.name, | ||
optional: optional, | ||
optional: value.optional || optional, | ||
typeAnnotation: typeAnnotation | ||
@@ -153,3 +154,3 @@ }; | ||
var propType = propDecl.type; | ||
var rawType = TypeChecker_1.typeToRNRawType(propType, info.sourceFile, true); | ||
var rawType = TypeChecker_1.typeToRNRawType(propType, info.sourceFile, { allowObject: true }); | ||
var _a = rnRawTypeToPropTypeTypeAnnotation(rawType, propType), optional = _a[0], typeAnnotation = _a[1]; | ||
@@ -156,0 +157,0 @@ return { |
@@ -14,3 +14,3 @@ import * as ts from 'typescript'; | ||
options: { | ||
[key: string]: boolean | string; | ||
[key: string]: boolean | string | string[]; | ||
}; | ||
@@ -17,0 +17,0 @@ } |
@@ -5,2 +5,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.tryParseExportCommand = exports.tryParseExportComponent = exports.tryParseExportNativeModule = exports.tryParseExport = exports.tryParseExportedCallExpression = exports.getMembersFromType = exports.resolveType = void 0; | ||
var ts = require("typescript"); | ||
@@ -188,2 +189,12 @@ function resolveType(typeNode, sourceFile) { | ||
} | ||
else if (ts.isArrayLiteralExpression(optionItem.initializer)) { | ||
var strings = []; | ||
for (var _i = 0, _a = optionItem.initializer.elements; _i < _a.length; _i++) { | ||
var element = _a[_i]; | ||
if (ts.isStringLiteral(element)) { | ||
strings.push(element.text); | ||
} | ||
} | ||
result.options[optionItem.name.getText()] = strings; | ||
} | ||
} | ||
@@ -190,0 +201,0 @@ }); |
@@ -8,3 +8,3 @@ import { SchemaType } from './CodegenSchema'; | ||
}>; | ||
export declare type Generators = 'descriptors' | 'events' | 'props' | 'tests' | 'shadow-nodes' | 'modules'; | ||
export declare type Generators = 'descriptors' | 'events' | 'props' | 'tests' | 'shadow-nodes' | 'modulesAndroid' | 'modulesCxx' | 'modulesIOS'; | ||
export declare type Config = Readonly<{ | ||
@@ -11,0 +11,0 @@ generators: Generators[]; |
@@ -5,3 +5,4 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.generate = void 0; | ||
exports.generate = (require('./rncodegen/src/generators/RNCodegen.js').generate); | ||
//# sourceMappingURL=ExportRNCodegen.js.map |
"use strict"; | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT license. | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.generator = exports.typeScriptToCodeSchema = void 0; | ||
var path = require("path"); | ||
@@ -10,2 +21,3 @@ var ts = require("typescript"); | ||
var NativeModuleParser_1 = require("./NativeModuleParser"); | ||
var TypeChecker_1 = require("./TypeChecker"); | ||
function messageChainToString(chain, indent) { | ||
@@ -88,6 +100,24 @@ var message = ''; | ||
} | ||
// 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] = { nativeModules: {} }; | ||
result.modules[moduleName].nativeModules[targetName === undefined ? info.name : targetName] = NativeModuleParser_1.processNativeModule(info); | ||
result.modules[moduleName] = NativeModuleParser_1.processNativeModule(info, aliases_1); | ||
return result; | ||
@@ -101,4 +131,3 @@ } | ||
var result = { modules: {} }; | ||
result.modules[moduleName] = { components: {} }; | ||
result.modules[moduleName].components[targetName === undefined ? info.name : targetName] = ComponentParser_1.processComponent(info, commandInfos[0]); | ||
result.modules[info.name] = ComponentParser_1.processComponent(info, commandInfos[0]); | ||
return result; | ||
@@ -108,2 +137,3 @@ } | ||
exports.typeScriptToCodeSchema = typeScriptToCodeSchema; | ||
__exportStar(require("./CodegenSchema"), exports); | ||
var rncodegen = require("./ExportRNCodegen"); | ||
@@ -110,0 +140,0 @@ var generator; |
import * as cs from './CodegenSchema'; | ||
import { ExportNativeModuleInfo } from './ExportParser'; | ||
export declare function processNativeModule(info: ExportNativeModuleInfo): cs.NativeModuleShape; | ||
import { RNRawType } from './RNRawType'; | ||
export interface NativeModuleAliases { | ||
aliases: { | ||
[key: string]: RNRawType; | ||
}; | ||
} | ||
export declare function processNativeModule(info: ExportNativeModuleInfo, nativeModuleAliases: NativeModuleAliases): cs.NativeModuleSchema; |
@@ -5,4 +5,5 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.processNativeModule = void 0; | ||
var TypeChecker_1 = require("./TypeChecker"); | ||
function rawTypeToParamType(rawType) { | ||
function rawTypeToBaseType(rawType, usedAliases) { | ||
switch (rawType.kind) { | ||
@@ -16,10 +17,9 @@ case 'String': return { type: 'StringTypeAnnotation' }; | ||
case 'js:Object': return { type: 'GenericObjectTypeAnnotation' }; | ||
case 'Any': return { type: 'AnyTypeAnnotation' }; | ||
case 'rn:RootTag': return { type: 'ReservedFunctionValueTypeAnnotation', name: 'RootTag' }; | ||
case 'Array': { | ||
if (rawType.elementType.kind === 'Union' || rawType.elementType.kind === 'Tuple') { | ||
var result = { type: 'ArrayTypeAnnotation' }; | ||
return result; | ||
if (rawType.elementType.kind === 'Union' || rawType.elementType.kind === 'Tuple' || rawType.elementType.kind === 'Any') { | ||
return { type: 'ArrayTypeAnnotation' }; | ||
} | ||
else { | ||
return { type: 'ArrayTypeAnnotation', elementType: rawTypeToParamType(rawType.elementType) }; | ||
return { type: 'ArrayTypeAnnotation', elementType: rawTypeToBaseType(rawType.elementType, usedAliases) }; | ||
} | ||
@@ -30,24 +30,17 @@ } | ||
properties: rawType.properties.map(function (param) { | ||
var propertyType = rawTypeToBaseType(param.propertyType, usedAliases); | ||
return { | ||
optional: param.propertyType.isNullable, | ||
optional: param.optional, | ||
name: param.name, | ||
typeAnnotation: rawTypeToParamType(param.propertyType) | ||
typeAnnotation: param.propertyType.isNullable ? { type: 'NullableTypeAnnotation', typeAnnotation: propertyType } : propertyType | ||
}; | ||
}) | ||
}; | ||
// What happened? | ||
// case 'Function': return { | ||
// type: 'FunctionTypeAnnotation', | ||
// params: rawType.parameters.map((param: { name: string; parameterType: RNRawType }) => { | ||
// return { | ||
// nullable: param.parameterType.isNullable, | ||
// name: param.name, | ||
// typeAnnotation: rawTypeToParamType(param.parameterType) | ||
// }; | ||
// }), | ||
// returnTypeAnnotation: rawTypeToReturnType(rawType.returnType) | ||
// }; | ||
case 'Function': return { | ||
type: 'FunctionTypeAnnotation' | ||
}; | ||
case 'Alias': { | ||
usedAliases.push(rawType.name); | ||
return { | ||
type: 'TypeAliasTypeAnnotation', | ||
name: rawType.name | ||
}; | ||
} | ||
default: | ||
@@ -62,20 +55,38 @@ } | ||
} | ||
function rawTypeToReturnType(rawType) { | ||
function rawTypeToParamType(rawType, usedAliases) { | ||
switch (rawType.kind) { | ||
case 'String': return { type: 'StringTypeAnnotation', nullable: rawType.isNullable }; | ||
case 'Number': return { type: 'NumberTypeAnnotation', nullable: rawType.isNullable }; | ||
case 'Int32': return { type: 'Int32TypeAnnotation', nullable: rawType.isNullable }; | ||
case 'Float': return { type: 'FloatTypeAnnotation', nullable: rawType.isNullable }; | ||
case 'Double': return { type: 'DoubleTypeAnnotation', nullable: rawType.isNullable }; | ||
case 'Boolean': return { type: 'BooleanTypeAnnotation', nullable: rawType.isNullable }; | ||
case 'js:Object': return { type: 'GenericObjectTypeAnnotation', nullable: rawType.isNullable }; | ||
case 'Function': return { | ||
type: 'FunctionTypeAnnotation', | ||
params: rawType.parameters.map(function (param) { | ||
var parameterType = rawTypeToParamType(param.parameterType, usedAliases); | ||
return { | ||
optional: param.optional, | ||
name: param.name, | ||
typeAnnotation: param.parameterType.isNullable ? { type: 'NullableTypeAnnotation', typeAnnotation: parameterType } : parameterType | ||
}; | ||
}), | ||
returnTypeAnnotation: rawTypeToReturnType(rawType.returnType, usedAliases) | ||
}; | ||
default: | ||
return rawTypeToBaseType(rawType, usedAliases); | ||
} | ||
} | ||
function rawTypeToReturnType(rawType, usedAliases) { | ||
switch (rawType.kind) { | ||
case 'String': return { type: 'StringTypeAnnotation' }; | ||
case 'Number': return { type: 'NumberTypeAnnotation' }; | ||
case 'Int32': return { type: 'Int32TypeAnnotation' }; | ||
case 'Float': return { type: 'FloatTypeAnnotation' }; | ||
case 'Double': return { type: 'DoubleTypeAnnotation' }; | ||
case 'Boolean': return { type: 'BooleanTypeAnnotation' }; | ||
case 'js:Object': return { type: 'GenericObjectTypeAnnotation' }; | ||
case 'rn:RootTag': return { type: 'ReservedFunctionValueTypeAnnotation', name: 'RootTag' }; | ||
case 'Void': | ||
case 'Null': return { type: 'VoidTypeAnnotation', nullable: rawType.isNullable }; | ||
case 'Null': return { type: 'VoidTypeAnnotation' }; | ||
case 'Array': { | ||
if (rawType.elementType.kind === 'Union' || rawType.elementType.kind === 'Tuple') { | ||
var result = { type: 'ArrayTypeAnnotation', nullable: rawType.isNullable }; | ||
return result; | ||
if (rawType.elementType.kind === 'Union' || rawType.elementType.kind === 'Tuple' || rawType.elementType.kind === 'Any') { | ||
return { type: 'ArrayTypeAnnotation' }; | ||
} | ||
else { | ||
return { type: 'ArrayTypeAnnotation', nullable: rawType.isNullable, elementType: rawTypeToParamType(rawType.elementType) }; | ||
return { type: 'ArrayTypeAnnotation', elementType: rawTypeToBaseType(rawType.elementType, usedAliases) }; | ||
} | ||
@@ -85,6 +96,5 @@ } | ||
// case 'js:Promise': return { type: 'GenericPromiseTypeAnnotation', nullable: rawType.isNullable, resolvedType: rawTypeToReturnType(rawType.elementType) }; | ||
case 'js:Promise': return { type: 'GenericPromiseTypeAnnotation', nullable: rawType.isNullable }; | ||
case 'js:Promise': return { type: 'PromiseTypeAnnotation' }; | ||
case 'Object': return { | ||
type: 'ObjectTypeAnnotation', | ||
nullable: rawType.isNullable, | ||
properties: rawType.properties.map(function (param) { | ||
@@ -94,6 +104,13 @@ return { | ||
name: param.name, | ||
typeAnnotation: rawTypeToParamType(param.propertyType) | ||
typeAnnotation: rawTypeToBaseType(param.propertyType, usedAliases) | ||
}; | ||
}) | ||
}; | ||
case 'Alias': { | ||
usedAliases.push(rawType.name); | ||
return { | ||
type: 'TypeAliasTypeAnnotation', | ||
name: rawType.name | ||
}; | ||
} | ||
default: | ||
@@ -108,21 +125,4 @@ } | ||
} | ||
function rawTypeToFunctionTypeAnnotation(rawType, propName, typeNode) { | ||
if (rawType.kind !== 'Function') { | ||
throw new Error("Member " + propName + " in a native module type " + typeNode.getText() + " is expected to be a function."); | ||
} | ||
return { | ||
type: 'FunctionTypeAnnotation', | ||
params: rawType.parameters.map(function (param) { | ||
return { | ||
nullable: param.parameterType.isNullable, | ||
name: param.name, | ||
typeAnnotation: rawTypeToParamType(param.parameterType) | ||
}; | ||
}), | ||
returnTypeAnnotation: rawTypeToReturnType(rawType.returnType), | ||
optional: rawType.isNullable | ||
}; | ||
} | ||
function processNativeModule(info) { | ||
var rawType = TypeChecker_1.typeToRNRawType(info.typeNode, info.sourceFile, true); | ||
function processNativeModule(info, nativeModuleAliases) { | ||
var rawType = TypeChecker_1.typeToRNRawType(info.typeNode, info.sourceFile, { allowObject: true, knownAliases: Object.keys(nativeModuleAliases.aliases) }); | ||
if (rawType.kind !== 'Object') { | ||
@@ -132,2 +132,3 @@ throw new Error("An object type is expected as a native module: " + info.typeNode.getText() + "."); | ||
var properties = []; | ||
var usedAliases = []; | ||
for (var _i = 0, _a = rawType.properties; _i < _a.length; _i++) { | ||
@@ -144,10 +145,41 @@ var prop = _a[_i]; | ||
} | ||
if (prop.propertyType.kind !== 'Function') { | ||
throw new Error("Member " + prop.name + " in a native module type " + info.typeNode.getText() + " is expected to be a function."); | ||
} | ||
properties.push({ | ||
name: prop.name, | ||
typeAnnotation: rawTypeToFunctionTypeAnnotation(prop.propertyType, prop.name, info.typeNode) | ||
optional: prop.optional, | ||
typeAnnotation: rawTypeToParamType(prop.propertyType, usedAliases) | ||
}); | ||
} | ||
return { properties: properties }; | ||
var aliases = {}; | ||
var writableAliases = aliases; | ||
Object.keys(nativeModuleAliases.aliases).forEach(function (key) { | ||
var rnRawType = nativeModuleAliases.aliases[key]; | ||
if (rnRawType !== undefined) { | ||
writableAliases[key] = rawTypeToParamType(rnRawType, usedAliases); | ||
} | ||
}); | ||
Object.keys(nativeModuleAliases.aliases).forEach(function (key) { | ||
if (usedAliases.indexOf(key) === -1) { | ||
delete writableAliases[key]; | ||
} | ||
}); | ||
var spec = { properties: properties }; | ||
var moduleNames = [info.name]; | ||
var excludedPlatforms = []; | ||
if (info.name.endsWith('Android')) { | ||
excludedPlatforms.push('iOS'); | ||
} | ||
if (info.name.endsWith('IOS')) { | ||
excludedPlatforms.push('android'); | ||
} | ||
if (excludedPlatforms.length === 0) { | ||
return { type: 'NativeModule', aliases: aliases, spec: spec, moduleNames: moduleNames }; | ||
} | ||
else { | ||
return { type: 'NativeModule', aliases: aliases, spec: spec, moduleNames: moduleNames, excludedPlatforms: excludedPlatforms }; | ||
} | ||
} | ||
exports.processNativeModule = processNativeModule; | ||
//# sourceMappingURL=NativeModuleParser.js.map |
@@ -13,2 +13,4 @@ /** | ||
@@ -198,92 +200,3 @@ | ||
// Union with more custom types. | ||
@@ -314,4 +227,4 @@ | ||
@@ -337,12 +250,164 @@ | ||
/** | ||
* NativeModule Types | ||
*/ | ||
// Union with more custom types. |
@@ -70,3 +70,3 @@ /** | ||
case 'ImageSourcePrimitive': | ||
imports.add('#include <react/components/image/conversions.h>'); | ||
imports.add('#include <react/renderer/components/image/conversions.h>'); | ||
return; | ||
@@ -73,0 +73,0 @@ default: |
@@ -24,2 +24,4 @@ /** | ||
* LICENSE file in the root directory of this source tree. | ||
* | ||
* ${'@'}generated by codegen project: GenerateComponentDescriptorH.js | ||
*/ | ||
@@ -29,4 +31,4 @@ | ||
#include <react/components/::_LIBRARY_::/ShadowNodes.h> | ||
#include <react/core/ConcreteComponentDescriptor.h> | ||
#include <react/renderer/components/::_LIBRARY_::/ShadowNodes.h> | ||
#include <react/renderer/core/ConcreteComponentDescriptor.h> | ||
@@ -51,2 +53,3 @@ namespace facebook { | ||
moduleSpecName , | ||
packageName , | ||
) { | ||
@@ -57,3 +60,8 @@ const fileName = 'ComponentDescriptors.h'; | ||
.map(moduleName => { | ||
const components = schema.modules[moduleName].components; | ||
const module = schema.modules[moduleName]; | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
const {components} = module; | ||
// No components in this module | ||
@@ -60,0 +68,0 @@ if (components == null) { |
@@ -91,2 +91,4 @@ /** | ||
* LICENSE file in the root directory of this source tree. | ||
* | ||
* ${'@'}generated by codegen project: GenerateComponentHObjCpp.js | ||
*/ | ||
@@ -327,2 +329,3 @@ | ||
moduleSpecName , | ||
packageName , | ||
) { | ||
@@ -333,3 +336,8 @@ const fileName = 'RCTComponentViewHelpers.h'; | ||
.map(moduleName => { | ||
const components = schema.modules[moduleName].components; | ||
const module = schema.modules[moduleName]; | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
const {components} = module; | ||
// No components in this module | ||
@@ -343,3 +351,6 @@ if (components == null) { | ||
const component = components[componentName]; | ||
return component.excludedPlatform !== 'iOS'; | ||
return !( | ||
component.excludedPlatforms && | ||
component.excludedPlatforms.includes('iOS') | ||
); | ||
}) | ||
@@ -346,0 +357,0 @@ .map(componentName => { |
@@ -35,5 +35,7 @@ /** | ||
* LICENSE file in the root directory of this source tree. | ||
* | ||
* ${'@'}generated by codegen project: GenerateEventEmitterCpp.js | ||
*/ | ||
#include <react/components/::_LIBRARY_::/EventEmitters.h> | ||
#include <react/renderer/components/::_LIBRARY_::/EventEmitters.h> | ||
@@ -170,6 +172,12 @@ namespace facebook { | ||
moduleSpecName , | ||
packageName , | ||
) { | ||
const moduleComponents = Object.keys(schema.modules) | ||
.map(moduleName => { | ||
const components = schema.modules[moduleName].components; | ||
const module = schema.modules[moduleName]; | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
const {components} = module; | ||
// No components in this module | ||
@@ -176,0 +184,0 @@ if (components == null) { |
@@ -43,6 +43,8 @@ /** | ||
* LICENSE file in the root directory of this source tree. | ||
* | ||
* ${'@'}generated by codegen project: GenerateEventEmitterH.js | ||
*/ | ||
#pragma once | ||
#include <react/components/view/ViewEventEmitter.h> | ||
#include <react/renderer/components/view/ViewEventEmitter.h> | ||
@@ -238,6 +240,12 @@ namespace facebook { | ||
moduleSpecName , | ||
packageName , | ||
) { | ||
const moduleComponents = Object.keys(schema.modules) | ||
.map(moduleName => { | ||
const components = schema.modules[moduleName].components; | ||
const module = schema.modules[moduleName]; | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
const {components} = module; | ||
// No components in this module | ||
@@ -244,0 +252,0 @@ if (components == null) { |
@@ -25,5 +25,7 @@ /** | ||
* LICENSE file in the root directory of this source tree. | ||
* | ||
* ${'@'}generated by codegen project: GeneratePropsCpp.js | ||
*/ | ||
#include <react/components/::_LIBRARY_::/Props.h> | ||
#include <react/renderer/components/::_LIBRARY_::/Props.h> | ||
::_IMPORTS_:: | ||
@@ -88,6 +90,7 @@ | ||
moduleSpecName , | ||
packageName , | ||
) { | ||
const fileName = 'Props.cpp'; | ||
const allImports = new Set([ | ||
'#include <react/core/propsConversions.h>', | ||
'#include <react/renderer/core/propsConversions.h>', | ||
]); | ||
@@ -97,3 +100,8 @@ | ||
.map(moduleName => { | ||
const components = schema.modules[moduleName].components; | ||
const module = schema.modules[moduleName]; | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
const {components} = module; | ||
// No components in this module | ||
@@ -107,3 +115,6 @@ if (components == null) { | ||
const component = components[componentName]; | ||
return component.excludedPlatform !== 'iOS'; | ||
return !( | ||
component.excludedPlatforms && | ||
component.excludedPlatforms.includes('iOS') | ||
); | ||
}) | ||
@@ -110,0 +121,0 @@ .map(componentName => { |
@@ -40,2 +40,4 @@ /** | ||
* LICENSE file in the root directory of this source tree. | ||
* | ||
* ${'@'}generated by codegen project: GeneratePropsH.js | ||
*/ | ||
@@ -471,3 +473,5 @@ #pragma once | ||
case 'ReactNativeCoreViewProps': | ||
imports.add('#include <react/components/view/ViewProps.h>'); | ||
imports.add( | ||
'#include <react/renderer/components/view/ViewProps.h>', | ||
); | ||
return; | ||
@@ -495,12 +499,12 @@ default: | ||
case 'ColorPrimitive': | ||
imports.add('#include <react/graphics/Color.h>'); | ||
imports.add('#include <react/renderer/graphics/Color.h>'); | ||
return; | ||
case 'ImageSourcePrimitive': | ||
imports.add('#include <react/imagemanager/primitives.h>'); | ||
imports.add('#include <react/renderer/imagemanager/primitives.h>'); | ||
return; | ||
case 'PointPrimitive': | ||
imports.add('#include <react/graphics/Geometry.h>'); | ||
imports.add('#include <react/renderer/graphics/Geometry.h>'); | ||
return; | ||
case 'EdgeInsetsPrimitive': | ||
imports.add('#include <react/graphics/Geometry.h>'); | ||
imports.add('#include <react/renderer/graphics/Geometry.h>'); | ||
return; | ||
@@ -546,3 +550,3 @@ default: | ||
if (typeAnnotation.type === 'ObjectTypeAnnotation') { | ||
imports.add('#include <react/core/propsConversions.h>'); | ||
imports.add('#include <react/renderer/core/propsConversions.h>'); | ||
const objectImports = getImports(typeAnnotation.properties); | ||
@@ -759,2 +763,3 @@ const localImports = getLocalImports(typeAnnotation.properties); | ||
moduleSpecName , | ||
packageName , | ||
) { | ||
@@ -767,3 +772,8 @@ const fileName = 'Props.h'; | ||
.map(moduleName => { | ||
const components = schema.modules[moduleName].components; | ||
const module = schema.modules[moduleName]; | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
const {components} = module; | ||
// No components in this module | ||
@@ -777,3 +787,6 @@ if (components == null) { | ||
const component = components[componentName]; | ||
return component.excludedPlatform !== 'iOS'; | ||
return !( | ||
component.excludedPlatforms && | ||
component.excludedPlatforms.includes('iOS') | ||
); | ||
}) | ||
@@ -780,0 +793,0 @@ .map(componentName => { |
@@ -58,3 +58,4 @@ /** | ||
const commandsTemplate = ` | ||
public void receiveCommand(::_INTERFACE_CLASSNAME_::<T> viewManager, T view, String commandName, ReadableArray args) { | ||
@Override | ||
public void receiveCommand(T view, String commandName, ReadableArray args) { | ||
switch (commandName) { | ||
@@ -202,3 +203,3 @@ ::_COMMAND_CASES_:: | ||
return `case "${command.name}": | ||
viewManager.${toSafeJavaString( | ||
mViewManager.${toSafeJavaString( | ||
command.name, | ||
@@ -267,6 +268,12 @@ false, | ||
moduleSpecName , | ||
packageName , | ||
) { | ||
const files = new Map(); | ||
Object.keys(schema.modules).forEach(moduleName => { | ||
const components = schema.modules[moduleName].components; | ||
const module = schema.modules[moduleName]; | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
const {components} = module; | ||
// No components in this module | ||
@@ -280,3 +287,6 @@ if (components == null) { | ||
const component = components[componentName]; | ||
return component.excludedPlatform !== 'android'; | ||
return !( | ||
component.excludedPlatforms && | ||
component.excludedPlatforms.includes('android') | ||
); | ||
}) | ||
@@ -283,0 +293,0 @@ .forEach(componentName => { |
@@ -212,6 +212,13 @@ /** | ||
moduleSpecName , | ||
packageName , | ||
) { | ||
const files = new Map(); | ||
Object.keys(schema.modules).forEach(moduleName => { | ||
const components = schema.modules[moduleName].components; | ||
const module = schema.modules[moduleName]; | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
const {components} = module; | ||
// No components in this module | ||
@@ -225,3 +232,6 @@ if (components == null) { | ||
const component = components[componentName]; | ||
return component.excludedPlatform !== 'android'; | ||
return !( | ||
component.excludedPlatforms && | ||
component.excludedPlatforms.includes('android') | ||
); | ||
}) | ||
@@ -228,0 +238,0 @@ .forEach(componentName => { |
@@ -24,5 +24,7 @@ /** | ||
* LICENSE file in the root directory of this source tree. | ||
* | ||
* ${'@'}generated by codegen project: GenerateShadowNodeCpp.js | ||
*/ | ||
#include <react/components/::_LIBRARY_::/ShadowNodes.h> | ||
#include <react/renderer/components/::_LIBRARY_::/ShadowNodes.h> | ||
@@ -47,2 +49,3 @@ namespace facebook { | ||
moduleSpecName , | ||
packageName , | ||
) { | ||
@@ -53,3 +56,8 @@ const fileName = 'ShadowNodes.cpp'; | ||
.map(moduleName => { | ||
const components = schema.modules[moduleName].components; | ||
const module = schema.modules[moduleName]; | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
const {components} = module; | ||
// No components in this module | ||
@@ -56,0 +64,0 @@ if (components == null) { |
@@ -24,2 +24,4 @@ /** | ||
* LICENSE file in the root directory of this source tree. | ||
* | ||
* ${'@'}generated by codegen project: GenerateShadowNodeH.js | ||
*/ | ||
@@ -29,4 +31,4 @@ | ||
::_IMPORTS_::#include <react/components/::_LIBRARY_::/Props.h> | ||
#include <react/components/view/ConcreteViewShadowNode.h> | ||
::_IMPORTS_::#include <react/renderer/components/::_LIBRARY_::/Props.h> | ||
#include <react/renderer/components/view/ConcreteViewShadowNode.h> | ||
@@ -58,2 +60,3 @@ namespace facebook { | ||
moduleSpecName , | ||
packageName , | ||
) { | ||
@@ -66,3 +69,8 @@ const fileName = 'ShadowNodes.h'; | ||
.map(moduleName => { | ||
const components = schema.modules[moduleName].components; | ||
const module = schema.modules[moduleName]; | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
const {components} = module; | ||
// No components in this module | ||
@@ -101,3 +109,3 @@ if (components == null) { | ||
const eventEmitterImport = `#include <react/components/${libraryName}/EventEmitters.h>\n`; | ||
const eventEmitterImport = `#include <react/renderer/components/${libraryName}/EventEmitters.h>\n`; | ||
@@ -104,0 +112,0 @@ const replacedTemplate = template |
@@ -32,6 +32,8 @@ /** | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
* | ||
* ${'@'}generated by codegen project: GenerateTests.js | ||
* */ | ||
#include <gtest/gtest.h> | ||
#include <react/components/::_LIBRARY_NAME_::/Props.h> | ||
#include <react/renderer/components/::_LIBRARY_NAME_::/Props.h> | ||
::_IMPORTS_:: | ||
@@ -77,2 +79,3 @@ | ||
}); | ||
// $FlowFixMe[incompatible-type] | ||
} else if (typeAnnotation.type === 'IntegerTypeAnnotation') { | ||
@@ -142,8 +145,9 @@ cases.push({ | ||
moduleSpecName , | ||
packageName , | ||
) { | ||
const fileName = 'Tests.cpp'; | ||
const allImports = new Set([ | ||
'#include <react/core/propsConversions.h>', | ||
'#include <react/core/RawProps.h>', | ||
'#include <react/core/RawPropsParser.h>', | ||
'#include <react/renderer/core/propsConversions.h>', | ||
'#include <react/renderer/core/RawProps.h>', | ||
'#include <react/renderer/core/RawPropsParser.h>', | ||
]); | ||
@@ -153,3 +157,8 @@ | ||
.map(moduleName => { | ||
const components = schema.modules[moduleName].components; | ||
const module = schema.modules[moduleName]; | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
const {components} = module; | ||
if (components == null) { | ||
@@ -156,0 +165,0 @@ return null; |
@@ -28,2 +28,4 @@ /** | ||
* @flow | ||
* | ||
* ${'@'}generated by codegen project: GenerateViewConfigJs.js | ||
*/ | ||
@@ -339,10 +341,11 @@ | ||
.map(moduleName => { | ||
const components = schema.modules[moduleName].components; | ||
// No components in this module | ||
if (components == null) { | ||
return null; | ||
const module = schema.modules[moduleName]; | ||
if (module.type !== 'Component') { | ||
return; | ||
} | ||
const {components} = module; | ||
return Object.keys(components) | ||
.map(componentName => { | ||
.map((componentName ) => { | ||
const component = components[componentName]; | ||
@@ -349,0 +352,0 @@ |
@@ -13,43 +13,79 @@ /** | ||
const {createAliasResolver, getModules} = require('./Utils'); | ||
const {unwrapNullable} = require('../../parsers/flow/modules/utils'); | ||
const propertyHeaderTemplate = | ||
'static jsi::Value __hostFunction_Native::_MODULE_NAME_::CxxSpecJSI_::_PROPERTY_NAME_::(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {'; | ||
const HostFunctionTemplate = ({ | ||
hasteModuleName, | ||
methodName, | ||
isVoid, | ||
args, | ||
} | ||
) => { | ||
const methodCallArgs = ['rt', ...args].join(', '); | ||
const methodCall = `static_cast<${hasteModuleName}CxxSpecJSI *>(&turboModule)->${methodName}(${methodCallArgs});`; | ||
const propertyCastTemplate = | ||
'static_cast<Native::_MODULE_NAME_::CxxSpecJSI *>(&turboModule)->::_PROPERTY_NAME_::(rt::_ARGS_::);'; | ||
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 nonvoidPropertyTemplate = ` | ||
${propertyHeaderTemplate} | ||
return ${propertyCastTemplate} | ||
}`.trim(); | ||
const ModuleTemplate = ({ | ||
hasteModuleName, | ||
hostFunctions, | ||
moduleName, | ||
methods, | ||
} | ||
) => { | ||
return `${hostFunctions.join('\n')} | ||
const voidPropertyTemplate = ` | ||
${propertyHeaderTemplate} | ||
${propertyCastTemplate} | ||
return jsi::Value::undefined(); | ||
${hasteModuleName}CxxSpecJSI::${hasteModuleName}CxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker) | ||
: TurboModule("${moduleName}", jsInvoker) { | ||
${methods | ||
.map(({methodName, paramCount}) => { | ||
return ` methodMap_["${methodName}"] = MethodMetadata {${paramCount}, __hostFunction_${hasteModuleName}CxxSpecJSI_${methodName}};`; | ||
}) | ||
.join('\n')} | ||
}`; | ||
}; | ||
const proprertyDefTemplate = | ||
' methodMap_["::_PROPERTY_NAME_::"] = MethodMetadata {::_ARGS_COUNT_::, __hostFunction_Native::_MODULE_NAME_::CxxSpecJSI_::_PROPERTY_NAME_::};'; | ||
const moduleTemplate = ` | ||
::_MODULE_PROPERTIES_:: | ||
Native::_MODULE_NAME_::CxxSpecJSI::Native::_MODULE_NAME_::CxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker) | ||
: TurboModule("::_MODULE_NAME_::", jsInvoker) { | ||
::_PROPERTIES_MAP_:: | ||
}`.trim(); | ||
const template = ` | ||
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
const FileTemplate = ({ | ||
libraryName, | ||
modules, | ||
} | ||
) => { | ||
return `/** | ||
* ${'C'}opyright (c) Facebook, Inc. and its affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* | ||
* ${'@'}generated by codegen project: GenerateModuleH.js | ||
*/ | ||
#include <react/modules/::_LIBRARY_NAME_::/NativeModules.h> | ||
#include <react/modules/${libraryName}/NativeModules.h> | ||
@@ -59,3 +95,3 @@ namespace facebook { | ||
::_MODULES_:: | ||
${modules} | ||
@@ -66,17 +102,31 @@ | ||
`; | ||
}; | ||
function traverseArg(arg, index) { | ||
function serializeArg( | ||
arg , | ||
index , | ||
resolveAlias , | ||
) { | ||
function wrap(suffix) { | ||
return `args[${index}]${suffix}`; | ||
} | ||
const {typeAnnotation} = arg; | ||
switch (typeAnnotation.type) { | ||
const {typeAnnotation: nullableTypeAnnotation} = arg; | ||
const [typeAnnotation] = unwrapNullable ( | ||
nullableTypeAnnotation, | ||
); | ||
let realTypeAnnotation = typeAnnotation; | ||
if (realTypeAnnotation.type === 'TypeAliasTypeAnnotation') { | ||
realTypeAnnotation = resolveAlias(realTypeAnnotation.name); | ||
} | ||
switch (realTypeAnnotation.type) { | ||
case 'ReservedFunctionValueTypeAnnotation': | ||
switch (typeAnnotation.name) { | ||
switch (realTypeAnnotation.name) { | ||
case 'RootTag': | ||
return wrap('.getNumber()'); | ||
default: | ||
(typeAnnotation.name ); | ||
(realTypeAnnotation.name ); | ||
throw new Error( | ||
`Unknown prop type for "${arg.name}, found: ${typeAnnotation.name}"`, | ||
`Unknown prop type for "${arg.name}, found: ${realTypeAnnotation.name}"`, | ||
); | ||
@@ -89,3 +139,7 @@ } | ||
case 'NumberTypeAnnotation': | ||
return wrap('.getNumber()'); | ||
case 'FloatTypeAnnotation': | ||
return wrap('.getNumber()'); | ||
case 'DoubleTypeAnnotation': | ||
return wrap('.getNumber()'); | ||
case 'Int32TypeAnnotation': | ||
@@ -98,11 +152,9 @@ return wrap('.getNumber()'); | ||
case 'GenericObjectTypeAnnotation': | ||
return wrap('.getObject(rt)'); | ||
case 'ObjectTypeAnnotation': | ||
return wrap('.getObject(rt)'); | ||
case 'AnyTypeAnnotation': | ||
throw new Error(`Any type is not allowed in params for "${arg.name}"`); | ||
default: | ||
// TODO (T65847278): Figure out why this does not work. | ||
// (typeAnnotation.type: empty); | ||
(realTypeAnnotation.type ); | ||
throw new Error( | ||
`Unknown prop type for "${arg.name}, found: ${typeAnnotation.type}"`, | ||
`Unknown prop type for "${arg.name}, found: ${realTypeAnnotation.type}"`, | ||
); | ||
@@ -112,13 +164,23 @@ } | ||
function traverseProperty(property) { | ||
const propertyTemplate = | ||
property.typeAnnotation.returnTypeAnnotation.type === 'VoidTypeAnnotation' | ||
? voidPropertyTemplate | ||
: nonvoidPropertyTemplate; | ||
const traversedArgs = property.typeAnnotation.params | ||
.map(traverseArg) | ||
.join(', '); | ||
return propertyTemplate | ||
.replace(/::_PROPERTY_NAME_::/g, property.name) | ||
.replace(/::_ARGS_::/g, traversedArgs === '' ? '' : ', ' + traversedArgs); | ||
function serializePropertyIntoHostFunction( | ||
hasteModuleName , | ||
property , | ||
resolveAlias , | ||
) { | ||
const [ | ||
propertyTypeAnnotation, | ||
] = unwrapNullable ( | ||
property.typeAnnotation, | ||
); | ||
const isVoid = | ||
propertyTypeAnnotation.returnTypeAnnotation.type === 'VoidTypeAnnotation'; | ||
return HostFunctionTemplate({ | ||
hasteModuleName, | ||
methodName: property.name, | ||
isVoid, | ||
args: propertyTypeAnnotation.params.map((p, i) => | ||
serializeArg(p, i, resolveAlias), | ||
), | ||
}); | ||
} | ||
@@ -131,34 +193,38 @@ | ||
moduleSpecName , | ||
packageName , | ||
) { | ||
const nativeModules = Object.keys(schema.modules) | ||
.map(moduleName => { | ||
const modules = schema.modules[moduleName].nativeModules; | ||
if (modules == null) { | ||
return null; | ||
} | ||
const nativeModules = getModules(schema); | ||
return modules; | ||
}) | ||
.filter(Boolean) | ||
.reduce((acc, modules) => Object.assign(acc, modules), {}); | ||
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, | ||
), | ||
); | ||
const modules = Object.keys(nativeModules) | ||
.map(name => { | ||
const {properties} = nativeModules[name]; | ||
const traversedProperties = properties | ||
.map(property => traverseProperty(property)) | ||
.join('\n'); | ||
return moduleTemplate | ||
.replace(/::_MODULE_PROPERTIES_::/g, traversedProperties) | ||
.replace( | ||
'::_PROPERTIES_MAP_::', | ||
properties | ||
.map(({name: propertyName, typeAnnotation: {params}}) => | ||
proprertyDefTemplate | ||
.replace(/::_PROPERTY_NAME_::/g, propertyName) | ||
.replace(/::_ARGS_COUNT_::/g, params.length.toString()), | ||
) | ||
.join('\n'), | ||
) | ||
.replace(/::_MODULE_NAME_::/g, name); | ||
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, | ||
}; | ||
}, | ||
), | ||
}); | ||
}) | ||
@@ -168,7 +234,8 @@ .join('\n'); | ||
const fileName = 'NativeModules.cpp'; | ||
const replacedTemplate = template | ||
.replace(/::_MODULES_::/g, modules) | ||
.replace(/::_LIBRARY_NAME_::/g, libraryName); | ||
const replacedTemplate = FileTemplate({ | ||
modules, | ||
libraryName, | ||
}); | ||
return new Map([[fileName, replacedTemplate]]); | ||
}, | ||
}; |
@@ -14,25 +14,40 @@ /** | ||
const {createAliasResolver, getModules} = require('./Utils'); | ||
const {unwrapNullable} = require('../../parsers/flow/modules/utils'); | ||
const moduleTemplate = ` | ||
class JSI_EXPORT Native::_MODULE_NAME_::CxxSpecJSI : public TurboModule { | ||
const ModuleClassDeclarationTemplate = ({ | ||
hasteModuleName, | ||
moduleProperties, | ||
} ) => { | ||
return `class JSI_EXPORT ${hasteModuleName}CxxSpecJSI : public TurboModule { | ||
protected: | ||
Native::_MODULE_NAME_::CxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker); | ||
${hasteModuleName}CxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker); | ||
public: | ||
::_MODULE_PROPERTIES_:: | ||
${moduleProperties} | ||
};`; | ||
}; | ||
const template = ` | ||
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
const FileTemplate = ({ | ||
modules, | ||
} | ||
) => { | ||
return `/** | ||
* ${'C'}opyright (c) Facebook, Inc. and its affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* | ||
* ${'@'}generated by codegen project: GenerateModuleH.js | ||
*/ | ||
@@ -46,3 +61,3 @@ | ||
namespace react { | ||
::_MODULES_:: | ||
${modules} | ||
@@ -52,17 +67,25 @@ } // namespace react | ||
`; | ||
}; | ||
function translatePrimitiveJSTypeToCpp( | ||
typeAnnotation | ||
, | ||
nullableTypeAnnotation , | ||
createErrorMessage , | ||
resolveAlias , | ||
) { | ||
switch (typeAnnotation.type) { | ||
const [typeAnnotation] = unwrapNullable ( | ||
nullableTypeAnnotation, | ||
); | ||
let realTypeAnnotation = typeAnnotation; | ||
if (realTypeAnnotation.type === 'TypeAliasTypeAnnotation') { | ||
realTypeAnnotation = resolveAlias(realTypeAnnotation.name); | ||
} | ||
switch (realTypeAnnotation.type) { | ||
case 'ReservedFunctionValueTypeAnnotation': | ||
switch (typeAnnotation.name) { | ||
switch (realTypeAnnotation.name) { | ||
case 'RootTag': | ||
return 'double'; | ||
default: | ||
(typeAnnotation.name ); | ||
throw new Error(createErrorMessage(typeAnnotation.name)); | ||
(realTypeAnnotation.name ); | ||
throw new Error(createErrorMessage(realTypeAnnotation.name)); | ||
} | ||
@@ -74,2 +97,5 @@ case 'VoidTypeAnnotation': | ||
case 'NumberTypeAnnotation': | ||
return 'double'; | ||
case 'DoubleTypeAnnotation': | ||
return 'double'; | ||
case 'FloatTypeAnnotation': | ||
@@ -82,2 +108,3 @@ return 'double'; | ||
case 'GenericObjectTypeAnnotation': | ||
return 'jsi::Object'; | ||
case 'ObjectTypeAnnotation': | ||
@@ -89,8 +116,7 @@ return 'jsi::Object'; | ||
return 'jsi::Function'; | ||
case 'GenericPromiseTypeAnnotation': | ||
case 'PromiseTypeAnnotation': | ||
return 'jsi::Value'; | ||
default: | ||
// TODO (T65847278): Figure out why this does not work. | ||
// (typeAnnotation.type: empty); | ||
throw new Error(createErrorMessage(typeAnnotation.type)); | ||
(realTypeAnnotation.type ); | ||
throw new Error(createErrorMessage(realTypeAnnotation.type)); | ||
} | ||
@@ -107,21 +133,22 @@ } | ||
moduleSpecName , | ||
packageName , | ||
) { | ||
const nativeModules = Object.keys(schema.modules) | ||
.map(moduleName => { | ||
const modules = schema.modules[moduleName].nativeModules; | ||
if (modules == null) { | ||
return null; | ||
} | ||
const nativeModules = getModules(schema); | ||
return modules; | ||
}) | ||
.filter(Boolean) | ||
.reduce((acc, components) => Object.assign(acc, components), {}); | ||
const modules = Object.keys(nativeModules) | ||
.map(hasteModuleName => { | ||
const { | ||
aliases, | ||
spec: {properties}, | ||
} = nativeModules[hasteModuleName]; | ||
const resolveAlias = createAliasResolver(aliases); | ||
const modules = Object.keys(nativeModules) | ||
.map(name => { | ||
const {properties} = nativeModules[name]; | ||
const traversedProperties = properties | ||
.map(prop => { | ||
const traversedArgs = prop.typeAnnotation.params | ||
const [ | ||
propTypeAnnotation, | ||
] = unwrapNullable ( | ||
prop.typeAnnotation, | ||
); | ||
const traversedArgs = propTypeAnnotation.params | ||
.map(param => { | ||
@@ -132,2 +159,3 @@ const translatedParam = translatePrimitiveJSTypeToCpp( | ||
`Unsupported type for param "${param.name}" in ${prop.name}. Found: ${typeName}`, | ||
resolveAlias, | ||
); | ||
@@ -147,5 +175,6 @@ const isObject = translatedParam.startsWith('jsi::'); | ||
translatePrimitiveJSTypeToCpp( | ||
prop.typeAnnotation.returnTypeAnnotation, | ||
propTypeAnnotation.returnTypeAnnotation, | ||
typeName => | ||
`Unsupported return type for ${prop.name}. Found: ${typeName}`, | ||
resolveAlias, | ||
), | ||
@@ -159,6 +188,7 @@ ) | ||
.join('\n'); | ||
return moduleTemplate | ||
.replace(/::_MODULE_PROPERTIES_::/g, traversedProperties) | ||
.replace(/::_MODULE_NAME_::/g, name) | ||
.replace('::_PROPERTIES_MAP_::', ''); | ||
return ModuleClassDeclarationTemplate({ | ||
hasteModuleName, | ||
moduleProperties: traversedProperties, | ||
}); | ||
}) | ||
@@ -168,3 +198,3 @@ .join('\n'); | ||
const fileName = 'NativeModules.h'; | ||
const replacedTemplate = template.replace(/::_MODULES_::/g, modules); | ||
const replacedTemplate = FileTemplate({modules}); | ||
@@ -171,0 +201,0 @@ return new Map([[fileName, replacedTemplate]]); |
@@ -28,4 +28,6 @@ /** | ||
const generateModuleCpp = require('./modules/GenerateModuleCpp.js'); | ||
const generateModuleHObjCpp = require('./modules/GenerateModuleHObjCpp.js'); | ||
const generateModuleMm = require('./modules/GenerateModuleMm.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'); | ||
@@ -47,2 +49,3 @@ const generatePropsJavaDelegate = require('./components/GeneratePropsJavaDelegate.js'); | ||
@@ -56,3 +59,5 @@ | ||
@@ -66,8 +71,3 @@ | ||
descriptors: [generateComponentDescriptorH.generate], | ||
events: [ | ||
generateEventEmitterCpp.generate, | ||
generateEventEmitterH.generate, | ||
generateModuleHObjCpp.generate, | ||
generateModuleMm.generate, | ||
], | ||
events: [generateEventEmitterCpp.generate, generateEventEmitterH.generate], | ||
props: [ | ||
@@ -80,3 +80,10 @@ generateComponentHObjCpp.generate, | ||
], | ||
modules: [generateModuleCpp.generate, generateModuleH.generate], | ||
// TODO: Refactor this to consolidate various C++ output variation instead of forking per platform. | ||
modulesAndroid: [ | ||
GenerateModuleJniCpp.generate, | ||
GenerateModuleJniH.generate, | ||
generateModuleJavaSpec.generate, | ||
], | ||
modulesCxx: [generateModuleCpp.generate, generateModuleH.generate], | ||
modulesIOS: [generateModuleObjCpp.generate], | ||
tests: [generateTests.generate], | ||
@@ -94,2 +101,6 @@ 'shadow-nodes': [ | ||
const location = path.join(outputDir, fileName); | ||
const dirName = path.dirname(location); | ||
if (!fs.existsSync(dirName)) { | ||
fs.mkdirSync(dirName, {recursive: true}); | ||
} | ||
fs.writeFileSync(location, contents); | ||
@@ -126,3 +137,9 @@ } catch (error) { | ||
generate( | ||
{libraryName, schema, outputDirectory, moduleSpecName} , | ||
{ | ||
libraryName, | ||
schema, | ||
outputDirectory, | ||
moduleSpecName, | ||
packageName, | ||
} , | ||
{generators, test} , | ||
@@ -135,3 +152,5 @@ ) { | ||
for (const generator of GENERATORS[name]) { | ||
generatedFiles.push(...generator(libraryName, schema, moduleSpecName)); | ||
generatedFiles.push( | ||
...generator(libraryName, schema, moduleSpecName, packageName), | ||
); | ||
} | ||
@@ -138,0 +157,0 @@ } |
@@ -14,3 +14,3 @@ /** | ||
@@ -21,3 +21,3 @@ const {getValueFromTypes} = require('../utils.js'); | ||
function buildCommandSchema(property, types ) { | ||
function buildCommandSchema(property, types ) { | ||
const name = property.key.name; | ||
@@ -103,3 +103,3 @@ const optional = property.optional; | ||
commandTypeAST , | ||
types , | ||
types , | ||
) { | ||
@@ -106,0 +106,0 @@ return commandTypeAST |
@@ -55,2 +55,8 @@ /** | ||
}; | ||
case '$ReadOnly': | ||
return getPropertyType( | ||
name, | ||
optional, | ||
typeAnnotation.typeParameters.params[0], | ||
); | ||
case 'ObjectTypeAnnotation': | ||
@@ -57,0 +63,0 @@ return { |
@@ -14,5 +14,5 @@ /** | ||
function extendsForProp(prop , types ) { | ||
function extendsForProp(prop , types ) { | ||
if (!prop.argument) { | ||
@@ -42,3 +42,3 @@ console.log('null', prop); | ||
typeDefinition , | ||
types , | ||
types , | ||
) { | ||
@@ -57,3 +57,3 @@ return typeDefinition.filter( | ||
typeDefinition , | ||
types , | ||
types , | ||
) { | ||
@@ -60,0 +60,0 @@ return typeDefinition |
@@ -19,2 +19,3 @@ /** | ||
const {getExtendsProps, removeKnownExtends} = require('./extends'); | ||
const {getTypes} = require('../utils'); | ||
@@ -171,3 +172,3 @@ function findComponentConfig(ast) { | ||
// $FlowFixMe there's no flowtype for AST | ||
function processComponent(ast, types) { | ||
function buildComponentSchema(ast) { | ||
const { | ||
@@ -181,2 +182,4 @@ componentName, | ||
const types = getTypes(ast); | ||
const propProperties = getPropProperties(propsTypeName, types); | ||
@@ -211,3 +214,3 @@ const commandOptions = getCommandOptions(commandOptionsExpression); | ||
module.exports = { | ||
processComponent, | ||
buildComponentSchema, | ||
}; |
@@ -57,3 +57,9 @@ /** | ||
foundOptions = optionsExpression.properties.reduce((options, prop) => { | ||
options[prop.key.name] = prop.value.value; | ||
if (prop.value.type === 'ArrayExpression') { | ||
options[prop.key.name] = prop.value.elements.map( | ||
element => element.value, | ||
); | ||
} else { | ||
options[prop.key.name] = prop.value.value; | ||
} | ||
return options; | ||
@@ -60,0 +66,0 @@ }, {}); |
@@ -16,5 +16,8 @@ /** | ||
function getPropProperties(propsTypeName , types ) { | ||
function getPropProperties( | ||
propsTypeName , | ||
types , | ||
) { | ||
const typeAlias = types[propsTypeName]; | ||
@@ -325,3 +328,3 @@ try { | ||
function buildPropSchema(property, types ) { | ||
function buildPropSchema(property, types ) { | ||
const name = property.key.name; | ||
@@ -431,3 +434,3 @@ | ||
typeDefinition , | ||
types , | ||
types , | ||
) { | ||
@@ -462,3 +465,3 @@ return typeDefinition | ||
typeDefinition , | ||
types , | ||
types , | ||
) { | ||
@@ -465,0 +468,0 @@ return flattenProperties(typeDefinition, types) |
@@ -32,3 +32,3 @@ /** | ||
function buildComponentSchema({ | ||
function wrapComponentSchema({ | ||
filename, | ||
@@ -45,2 +45,3 @@ componentName, | ||
[filename]: { | ||
type: 'Component', | ||
components: { | ||
@@ -61,3 +62,3 @@ [componentName]: { | ||
module.exports = { | ||
buildComponentSchema, | ||
wrapComponentSchema, | ||
}; |
@@ -18,24 +18,10 @@ /** | ||
const path = require('path'); | ||
const {buildModuleSchema} = require('./modules/schema'); | ||
const {buildComponentSchema} = require('./components/schema'); | ||
const {processComponent} = require('./components'); | ||
const {processModule} = require('./modules'); | ||
const {buildComponentSchema} = require('./components'); | ||
const {wrapComponentSchema} = require('./components/schema'); | ||
const {buildModuleSchema} = require('./modules'); | ||
const {wrapModuleSchema} = require('./modules/schema'); | ||
const {createParserErrorCapturer} = require('./utils'); | ||
const invariant = require('invariant'); | ||
function getTypes(ast) { | ||
return ast.body.reduce((types, node) => { | ||
if (node.type === 'ExportNamedDeclaration') { | ||
if (node.declaration && node.declaration.type !== 'VariableDeclaration') { | ||
types[node.declaration.id.name] = node.declaration; | ||
} | ||
} else if ( | ||
node.type === 'TypeAlias' || | ||
node.type === 'InterfaceDeclaration' | ||
) { | ||
types[node.id.name] = node; | ||
} | ||
return types; | ||
}, {}); | ||
} | ||
function getConfigType(ast, types) { | ||
function isComponent(ast) { | ||
const defaultExports = ast.body.filter( | ||
@@ -45,36 +31,71 @@ node => node.type === 'ExportDefaultDeclaration', | ||
let isComponent = false; | ||
if (defaultExports.length === 0) { | ||
return false; | ||
} | ||
if (defaultExports.length > 0) { | ||
let declaration = defaultExports[0].declaration; | ||
// codegenNativeComponent can be nested inside a cast | ||
// expression so we need to go one level deeper | ||
if (declaration.type === 'TypeCastExpression') { | ||
declaration = declaration.expression; | ||
} | ||
let declaration = defaultExports[0].declaration; | ||
// codegenNativeComponent can be nested inside a cast | ||
// expression so we need to go one level deeper | ||
if (declaration.type === 'TypeCastExpression') { | ||
declaration = declaration.expression; | ||
} | ||
isComponent = | ||
declaration && | ||
declaration.callee && | ||
declaration.callee.name === 'codegenNativeComponent'; | ||
if (declaration.type !== 'CallExpression') { | ||
return false; | ||
} | ||
const typesExtendingTurboModule = Object.keys(types) | ||
.map(typeName => types[typeName]) | ||
.filter( | ||
type => | ||
type.extends && | ||
type.extends[0] && | ||
type.extends[0].id.name === 'TurboModule', | ||
); | ||
return ( | ||
declaration.callee.type === 'Identifier' && | ||
declaration.callee.name === 'codegenNativeComponent' | ||
); | ||
} | ||
if (typesExtendingTurboModule.length > 1) { | ||
function isModule( | ||
// TODO(T71778680): Flow-type this node. | ||
ast , | ||
) { | ||
const moduleInterfaces = ast.body | ||
.map(node => { | ||
if ( | ||
node.type === 'ExportNamedDeclaration' && | ||
node.exportKind === 'type' && | ||
node.declaration.type === 'InterfaceDeclaration' | ||
) { | ||
return node.declaration; | ||
} | ||
return node; | ||
}) | ||
.filter(declaration => { | ||
return ( | ||
declaration.type === 'InterfaceDeclaration' && | ||
declaration.extends.length === 1 && | ||
declaration.extends[0].type === 'InterfaceExtends' && | ||
declaration.extends[0].id.name === 'TurboModule' | ||
); | ||
}) | ||
.map(declaration => declaration.id.name); | ||
if (moduleInterfaces.length === 0) { | ||
return false; | ||
} | ||
if (moduleInterfaces.length > 1) { | ||
throw new Error( | ||
'Found two types extending "TurboModule" is one file. Split them into separated files.', | ||
'File contains declarations of more than one module: ' + | ||
moduleInterfaces.join(', ') + | ||
'. Please declare exactly one module in this file.', | ||
); | ||
} | ||
const isModule = typesExtendingTurboModule.length === 1; | ||
return true; | ||
} | ||
if (isModule && isComponent) { | ||
function getConfigType( | ||
// TODO(T71778680): Flow-type this node. | ||
ast , | ||
) { | ||
const isConfigAComponent = isComponent(ast); | ||
const isConfigAModule = isModule(ast); | ||
if (isConfigAModule && isConfigAComponent) { | ||
throw new Error( | ||
@@ -85,10 +106,11 @@ 'Found type extending "TurboModule" and exported "codegenNativeComponent" declaration in one file. Split them into separated files.', | ||
if (isModule) { | ||
if (isConfigAModule) { | ||
return 'module'; | ||
} else if (isComponent) { | ||
} else if (isConfigAComponent) { | ||
return 'component'; | ||
} else { | ||
throw new Error( | ||
`Default export for module specified incorrectly. It should containts | ||
either type extending "TurboModule" or "codegenNativeComponent".`, | ||
'File neither contains a module declaration, nor a component declaration. ' + | ||
'For module declarations, please make sure your file has an InterfaceDeclaration extending TurboModule. ' + | ||
'For component declarations, please make sure your file has a default export calling the codegenNativeComponent<Props>(...) macro.', | ||
); | ||
@@ -98,11 +120,29 @@ } | ||
function buildSchema(contents , filename ) { | ||
const withSpace = (...args) => args.join('\\s*'); | ||
/** | ||
* Parse the TurboModuleRegistry.get(Enforcing)? call using RegExp. | ||
* Why? This call can appear anywhere in the NativeModule spec. Currently, | ||
* there is no good way of traversing the AST to find the MemberExpression | ||
* responsible for the call. | ||
*/ | ||
const TURBO_MODULE_REGISTRY_REQUIRE_REGEX_STRING = withSpace( | ||
'TurboModuleRegistry', | ||
'\\.', | ||
'get(Enforcing)?', | ||
'<', | ||
'Spec', | ||
'>', | ||
'\\(', | ||
'[\'"](?<nativeModuleName>[A-Za-z$_0-9]+)[\'"]', | ||
',?', | ||
'\\)', | ||
); | ||
function buildSchema(contents , filename ) { | ||
const ast = flowParser.parse(contents); | ||
const types = getTypes(ast); | ||
const configType = getConfigType(ast); | ||
const configType = getConfigType(ast, types); | ||
if (configType === 'component') { | ||
return buildComponentSchema(processComponent(ast, types)); | ||
return wrapComponentSchema(buildComponentSchema(ast)); | ||
} else { | ||
@@ -112,8 +152,57 @@ if (filename === undefined || filename === null) { | ||
} | ||
const moduleName = path.basename(filename).slice(6, -3); | ||
return buildModuleSchema(processModule(types), moduleName); | ||
const hasteModuleName = path.basename(filename).replace(/\.js$/, ''); | ||
const regex = new RegExp(TURBO_MODULE_REGISTRY_REQUIRE_REGEX_STRING, 'g'); | ||
let match = regex.exec(contents); | ||
const errorHeader = `Error while parsing Module '${hasteModuleName}'`; | ||
if (match == null) { | ||
throw new Error( | ||
`${errorHeader}: No call to TurboModuleRegistry.get<Spec>('...') detected.`, | ||
); | ||
} | ||
const moduleNames = []; | ||
while (match != null) { | ||
const resultGroups = match.groups; | ||
invariant( | ||
resultGroups != null, | ||
`Couldn't parse TurboModuleRegistry.(get|getEnforcing)<Spec> call in module '${hasteModuleName}'.`, | ||
); | ||
if (!moduleNames.includes(resultGroups.nativeModuleName)) { | ||
moduleNames.push(resultGroups.nativeModuleName); | ||
} | ||
match = regex.exec(contents); | ||
} | ||
const [parsingErrors, guard] = createParserErrorCapturer(); | ||
const schema = guard(() => | ||
buildModuleSchema(hasteModuleName, moduleNames, ast, guard), | ||
); | ||
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); | ||
} | ||
} | ||
function parseFile(filename ) { | ||
function parseFile(filename ) { | ||
const contents = fs.readFileSync(filename, 'utf8'); | ||
@@ -124,3 +213,3 @@ | ||
function parseModuleFixture(filename ) { | ||
function parseModuleFixture(filename ) { | ||
const contents = fs.readFileSync(filename, 'utf8'); | ||
@@ -131,4 +220,4 @@ | ||
function parseString(contents ) { | ||
return buildSchema(contents); | ||
function parseString(contents , filename ) { | ||
return buildSchema(contents, filename); | ||
} | ||
@@ -135,0 +224,0 @@ |
@@ -13,36 +13,584 @@ /** | ||
const {getMethods} = require('./methods'); | ||
function getModuleProperties(types, interfaceName) { | ||
if (types[interfaceName] && types[interfaceName].body) { | ||
return types[interfaceName].body.properties; | ||
const {resolveTypeAnnotation, getTypes} = require('../utils.js'); | ||
const {unwrapNullable, wrapNullable} = require('./utils'); | ||
const { | ||
IncorrectlyParameterizedFlowGenericParserError, | ||
MisnamedModuleFlowInterfaceParserError, | ||
ModuleFlowInterfaceNotParserError, | ||
UnnamedFunctionParamParserError, | ||
UnsupportedArrayElementTypeAnnotationParserError, | ||
UnsupportedFlowGenericParserError, | ||
UnsupportedFlowTypeAnnotationParserError, | ||
UnsupportedFunctionParamTypeAnnotationParserError, | ||
UnsupportedFunctionReturnTypeAnnotationParserError, | ||
UnsupportedModulePropertyParserError, | ||
UnsupportedObjectPropertyTypeAnnotationParserError, | ||
UnsupportedObjectPropertyValueTypeAnnotationParserError, | ||
} = require('./errors.js'); | ||
const invariant = require('invariant'); | ||
function nullGuard (fn ) { | ||
return fn(); | ||
} | ||
function translateTypeAnnotation( | ||
hasteModuleName , | ||
/** | ||
* TODO(T71778680): Flow-type this node. | ||
*/ | ||
flowTypeAnnotation , | ||
types , | ||
aliasMap , | ||
guard , | ||
) { | ||
const { | ||
nullable, | ||
typeAnnotation, | ||
typeAliasResolutionStatus, | ||
} = resolveTypeAnnotation(flowTypeAnnotation, types); | ||
switch (typeAnnotation.type) { | ||
case 'GenericTypeAnnotation': { | ||
switch (typeAnnotation.id.name) { | ||
case 'RootTag': { | ||
return wrapNullable(nullable, { | ||
type: 'ReservedFunctionValueTypeAnnotation', | ||
name: 'RootTag', | ||
}); | ||
} | ||
case 'Promise': { | ||
assertGenericTypeAnnotationHasExactlyOneTypeParameter( | ||
hasteModuleName, | ||
typeAnnotation, | ||
); | ||
return wrapNullable(nullable, { | ||
type: 'PromiseTypeAnnotation', | ||
}); | ||
} | ||
case 'Array': | ||
case '$ReadOnlyArray': { | ||
assertGenericTypeAnnotationHasExactlyOneTypeParameter( | ||
hasteModuleName, | ||
typeAnnotation, | ||
); | ||
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, | ||
), | ||
); | ||
if (elementType.type === 'VoidTypeAnnotation') { | ||
throw new UnsupportedArrayElementTypeAnnotationParserError( | ||
hasteModuleName, | ||
typeAnnotation.typeParameters.params[0], | ||
typeAnnotation.type, | ||
'void', | ||
); | ||
} | ||
if (elementType.type === 'PromiseTypeAnnotation') { | ||
throw new UnsupportedArrayElementTypeAnnotationParserError( | ||
hasteModuleName, | ||
typeAnnotation.typeParameters.params[0], | ||
typeAnnotation.type, | ||
'Promise', | ||
); | ||
} | ||
if (elementType.type === 'FunctionTypeAnnotation') { | ||
throw new UnsupportedArrayElementTypeAnnotationParserError( | ||
hasteModuleName, | ||
typeAnnotation.typeParameters.params[0], | ||
typeAnnotation.type, | ||
'FunctionTypeAnnotation', | ||
); | ||
} | ||
const finalTypeAnnotation | ||
= { | ||
type: 'ArrayTypeAnnotation', | ||
elementType: wrapNullable(isElementTypeNullable, elementType), | ||
}; | ||
return wrapNullable(nullable, finalTypeAnnotation); | ||
} catch (ex) { | ||
return wrapNullable(nullable, { | ||
type: 'ArrayTypeAnnotation', | ||
}); | ||
} | ||
} | ||
case '$ReadOnly': { | ||
assertGenericTypeAnnotationHasExactlyOneTypeParameter( | ||
hasteModuleName, | ||
typeAnnotation, | ||
); | ||
return translateTypeAnnotation( | ||
hasteModuleName, | ||
typeAnnotation.typeParameters.params[0], | ||
types, | ||
aliasMap, | ||
guard, | ||
); | ||
} | ||
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 'Object': { | ||
return wrapNullable(nullable, { | ||
type: 'GenericObjectTypeAnnotation', | ||
}); | ||
} | ||
default: { | ||
throw new UnsupportedFlowGenericParserError( | ||
hasteModuleName, | ||
typeAnnotation, | ||
); | ||
} | ||
} | ||
} | ||
case 'ObjectTypeAnnotation': { | ||
const objectTypeAnnotation = { | ||
type: 'ObjectTypeAnnotation', | ||
properties: (typeAnnotation.properties ) | ||
.map (property => { | ||
return guard(() => { | ||
if (property.type !== 'ObjectTypeProperty') { | ||
throw new UnsupportedObjectPropertyTypeAnnotationParserError( | ||
hasteModuleName, | ||
property, | ||
property.type, | ||
); | ||
} | ||
const {optional, key} = property; | ||
const [ | ||
propertyTypeAnnotation, | ||
isPropertyNullable, | ||
] = unwrapNullable( | ||
translateTypeAnnotation( | ||
hasteModuleName, | ||
property.value, | ||
types, | ||
aliasMap, | ||
guard, | ||
), | ||
); | ||
if (propertyTypeAnnotation.type === 'FunctionTypeAnnotation') { | ||
throw new UnsupportedObjectPropertyValueTypeAnnotationParserError( | ||
hasteModuleName, | ||
property.value, | ||
property.key, | ||
propertyTypeAnnotation.type, | ||
); | ||
} | ||
if (propertyTypeAnnotation.type === 'VoidTypeAnnotation') { | ||
throw new UnsupportedObjectPropertyValueTypeAnnotationParserError( | ||
hasteModuleName, | ||
property.value, | ||
property.key, | ||
'void', | ||
); | ||
} | ||
if (propertyTypeAnnotation.type === 'PromiseTypeAnnotation') { | ||
throw new UnsupportedObjectPropertyValueTypeAnnotationParserError( | ||
hasteModuleName, | ||
property.value, | ||
property.key, | ||
'Promise', | ||
); | ||
} | ||
return { | ||
name: key.name, | ||
optional, | ||
typeAnnotation: wrapNullable( | ||
isPropertyNullable, | ||
propertyTypeAnnotation, | ||
), | ||
}; | ||
}); | ||
}) | ||
.filter(Boolean), | ||
}; | ||
if (!typeAliasResolutionStatus.successful) { | ||
return wrapNullable(nullable, objectTypeAnnotation); | ||
} | ||
/** | ||
* All aliases RHS are required. | ||
*/ | ||
aliasMap[typeAliasResolutionStatus.aliasName] = objectTypeAnnotation; | ||
/** | ||
* 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, | ||
guard, | ||
), | ||
); | ||
} | ||
default: { | ||
throw new UnsupportedFlowTypeAnnotationParserError( | ||
hasteModuleName, | ||
typeAnnotation, | ||
); | ||
} | ||
} | ||
throw new Error( | ||
`Interface properties for "${interfaceName}" has been specified incorrectly.`, | ||
} | ||
function assertGenericTypeAnnotationHasExactlyOneTypeParameter( | ||
moduleName: st /** | ||
* TODO(T71778680): This is a GenericTypeAnnotation. Flow type this node | ||
*/ | ||
typeAnnotation: $F { | ||
if (typeAnnotation.typeParameters == null) { | ||
throw new IncorrectlyParameterizedFlowGenericParserError( | ||
moduleName, | ||
typeAnnotation, | ||
); | ||
} | ||
invariant( | ||
typeAnnotation.typeParameters.type === 'TypeParameterInstantiation', | ||
"assertGenericTypeAnnotationHasExactlyOneTypeParameter: Type parameters must be an AST node of type 'TypeParameterInstantiation'", | ||
); | ||
if (typeAnnotation.typeParameters.params.length !== 1) { | ||
throw new IncorrectlyParameterizedFlowGenericParserError( | ||
moduleName, | ||
typeAnnotation, | ||
); | ||
} | ||
} | ||
function findInterfaceName(types) { | ||
return Object.keys(types) | ||
.map(typeName => types[typeName]) | ||
.filter( | ||
type => | ||
type.extends && | ||
type.extends[0] && | ||
type.extends[0].id.name === 'TurboModule', | ||
)[0].id.name; | ||
function translateFunctionTypeAnnotation( | ||
hasteModuleName: st // TODO(T71778680): This is a FunctionTypeAnnotation. Type this. | ||
flowFunctionTypeAnnotation: $F types: Ty aliasMap: {. guard: Pa Na const params: Ar ]; | ||
for (const flowParam of (flowFunctionTypeAnnotation.params: $R | ||
const parsedParam = guard(() => { | ||
if (flowParam.name == null) { | ||
throw new UnnamedFunctionParamParserError(flowParam, hasteModuleName); | ||
} | ||
const paramName = flowParam.name.name; | ||
const [ | ||
paramTypeAnnotation, | ||
isParamTypeAnnotationNullable, | ||
] = unwrapNullable( | ||
translateTypeAnnotation( | ||
hasteModuleName, | ||
flowParam.typeAnnotation, | ||
types, | ||
aliasMap, | ||
guard, | ||
), | ||
); | ||
if (paramTypeAnnotation.type === 'VoidTypeAnnotation') { | ||
throw new UnsupportedFunctionParamTypeAnnotationParserError( | ||
hasteModuleName, | ||
flowParam.typeAnnotation, | ||
paramName, | ||
'void', | ||
); | ||
} | ||
if (paramTypeAnnotation.type === 'PromiseTypeAnnotation') { | ||
throw new UnsupportedFunctionParamTypeAnnotationParserError( | ||
hasteModuleName, | ||
flowParam.typeAnnotation, | ||
paramName, | ||
'Promise', | ||
); | ||
} | ||
return { | ||
name: flowParam.name.name, | ||
optional: flowParam.optional, | ||
typeAnnotation: wrapNullable( | ||
isParamTypeAnnotationNullable, | ||
paramTypeAnnotation, | ||
), | ||
}; | ||
}); | ||
if (parsedParam != null) { | ||
params.push(parsedParam); | ||
} | ||
} | ||
const [returnTypeAnnotation, isReturnTypeAnnotationNullable] = unwrapNullable( | ||
translateTypeAnnotation( | ||
hasteModuleName, | ||
flowFunctionTypeAnnotation.returnType, | ||
types, | ||
aliasMap, | ||
guard, | ||
), | ||
); | ||
if (returnTypeAnnotation.type === 'FunctionTypeAnnotation') { | ||
throw new UnsupportedFunctionReturnTypeAnnotationParserError( | ||
hasteModuleName, | ||
flowFunctionTypeAnnotation.returnType, | ||
'FunctionTypeAnnotation', | ||
); | ||
} | ||
return { | ||
type: 'FunctionTypeAnnotation', | ||
returnTypeAnnotation: wrapNullable( | ||
isReturnTypeAnnotationNullable, | ||
returnTypeAnnotation, | ||
), | ||
params, | ||
}; | ||
} | ||
// $FlowFixMe there's no flowtype for AST | ||
function processModule(types) { | ||
const interfaceName = findInterfaceName(types); | ||
function buildPropertySchema( | ||
hasteModuleName: st // TODO(T71778680): This is an ObjectTypeProperty containing either: | ||
// - a FunctionTypeAnnotation or GenericTypeAnnotation | ||
// - a NullableTypeAnnoation containing a FunctionTypeAnnotation or GenericTypeAnnotation | ||
// Flow type this node | ||
property: $F types: Ty aliasMap: {. guard: Pa Na let nullable = false; | ||
let {key, value} = property; | ||
const moduleProperties = getModuleProperties(types, interfaceName); | ||
const properties = getMethods(moduleProperties, types); | ||
return {properties}; | ||
const methodName: st ey.name; | ||
({nullable, typeAnnotation: value} = resolveTypeAnnotation(value, types)); | ||
if (value.type !== 'FunctionTypeAnnotation') { | ||
throw new UnsupportedModulePropertyParserError( | ||
hasteModuleName, | ||
property.value, | ||
property.key.name, | ||
value.type, | ||
); | ||
} | ||
return { | ||
name: methodName, | ||
optional: property.optional, | ||
typeAnnotation: wrapNullable( | ||
nullable, | ||
translateFunctionTypeAnnotation( | ||
hasteModuleName, | ||
value, | ||
types, | ||
aliasMap, | ||
guard, | ||
), | ||
), | ||
}; | ||
} | ||
function buildModuleSchema( | ||
hasteModuleName: st moduleNames: $R /** | ||
* TODO(T71778680): Flow-type this node. | ||
*/ | ||
ast: $F guard: Pa Na const types = getTypes(ast); | ||
const moduleInterfaceNames = (Object.keys( | ||
types, | ||
): $R lter((typeName: st { | ||
const declaration = types[typeName]; | ||
return ( | ||
declaration.type === 'InterfaceDeclaration' && | ||
declaration.extends.length === 1 && | ||
declaration.extends[0].type === 'InterfaceExtends' && | ||
declaration.extends[0].id.name === 'TurboModule' | ||
); | ||
}); | ||
if (moduleInterfaceNames.length !== 1) { | ||
throw new ModuleFlowInterfaceNotParserError(hasteModuleName, ast); | ||
} | ||
const [moduleInterfaceName] = moduleInterfaceNames; | ||
if (moduleInterfaceName !== 'Spec') { | ||
throw new MisnamedModuleFlowInterfaceParserError( | ||
hasteModuleName, | ||
types[moduleInterfaceName].id, | ||
); | ||
} | ||
// 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'); | ||
} | ||
}); | ||
const declaration = types[moduleInterfaceName]; | ||
return (declaration.body.properties: $R .filter(property => property.type === 'ObjectTypeProperty') | ||
.map<?{| | ||
perty => { | ||
const aliasMap: {. }; | ||
return guard(() => ({ | ||
aliasMap: aliasMap, | ||
propertySchema: buildPropertySchema( | ||
hasteModuleName, | ||
property, | ||
types, | ||
aliasMap, | ||
guard, | ||
), | ||
})); | ||
}) | ||
.filter(Boolean) | ||
.reduce( | ||
(moduleSchema: Na liasMap, propertySchema}) => { | ||
return { | ||
type: 'NativeModule', | ||
aliases: {...moduleSchema.aliases, ...aliasMap}, | ||
spec: { | ||
properties: [...moduleSchema.spec.properties, propertySchema], | ||
}, | ||
moduleNames: moduleSchema.moduleNames, | ||
excludedPlatforms: moduleSchema.excludedPlatforms, | ||
}; | ||
}, | ||
{ | ||
type: 'NativeModule', | ||
aliases: {}, | ||
spec: {properties: []}, | ||
moduleNames: moduleNames, | ||
excludedPlatforms: | ||
excludedPlatforms.length !== 0 ? [...excludedPlatforms] : undefined, | ||
}, | ||
); | ||
} | ||
module.exports = { | ||
processModule, | ||
buildModuleSchema, | ||
}; |
@@ -8,3 +8,3 @@ /** | ||
* @format | ||
* strict-local | ||
* strict | ||
*/ | ||
@@ -14,24 +14,11 @@ | ||
function buildModuleSchema( | ||
{properties} , | ||
moduleName , | ||
function wrapModuleSchema( | ||
nativeModuleSchema , | ||
hasteModuleName , | ||
) { | ||
return { | ||
modules: { | ||
[`Native${moduleName}`]: { | ||
nativeModules: { | ||
[moduleName]: { | ||
properties, | ||
}, | ||
}, | ||
}, | ||
[hasteModuleName]: nativeModuleSchema, | ||
}, | ||
@@ -42,3 +29,3 @@ }; | ||
module.exports = { | ||
buildModuleSchema, | ||
wrapModuleSchema, | ||
}; |
@@ -13,9 +13,100 @@ /** | ||
// $FlowFixMe there's no flowtype for ASTs | ||
const {ParserError} = require('./errors'); | ||
/** | ||
* This FlowFixMe is supposed to refer to an InterfaceDeclaration or TypeAlias | ||
* declaration type. Unfortunately, we don't have those types, because flow-parser | ||
* generates them, and flow-parser is not type-safe. In the future, we should find | ||
* a way to get these types from our flow parser library. | ||
* | ||
* TODO(T71778680): Flow type AST Nodes | ||
*/ | ||
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' | ||
) { | ||
types[node.declaration.id.name] = node.declaration; | ||
} | ||
} else if ( | ||
node.type === 'TypeAlias' || | ||
node.type === 'InterfaceDeclaration' | ||
) { | ||
types[node.id.name] = node; | ||
} | ||
return types; | ||
}, {}); | ||
} | ||
// $FlowFixMe there's no flowtype for ASTs | ||
function getValueFromTypes(value , types ) { | ||
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', | ||
); | ||
let node = typeAnnotation; | ||
let nullable = false; | ||
let typeAliasResolutionStatus = { | ||
successful: false, | ||
}; | ||
for (;;) { | ||
if (node.type === 'NullableTypeAnnotation') { | ||
nullable = true; | ||
node = node.typeAnnotation; | ||
} else if (node.type === 'GenericTypeAnnotation') { | ||
typeAliasResolutionStatus = { | ||
successful: true, | ||
aliasName: node.id.name, | ||
}; | ||
const resolvedTypeAnnotation = types[node.id.name]; | ||
if (resolvedTypeAnnotation == null) { | ||
break; | ||
} | ||
invariant( | ||
resolvedTypeAnnotation.type === 'TypeAlias', | ||
`GenericTypeAnnotation '${node.id.name}' must resolve to a TypeAlias. Instead, it resolved to a '${resolvedTypeAnnotation.type}'`, | ||
); | ||
node = resolvedTypeAnnotation.right; | ||
} else { | ||
break; | ||
} | ||
} | ||
return { | ||
nullable: nullable, | ||
typeAnnotation: node, | ||
typeAliasResolutionStatus, | ||
}; | ||
} | ||
function getValueFromTypes(value , types ) { | ||
if (value.type === 'GenericTypeAnnotation' && types[value.id.name]) { | ||
@@ -27,4 +118,30 @@ return getValueFromTypes(types[value.id.name].right, types); | ||
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]; | ||
} | ||
module.exports = { | ||
getValueFromTypes, | ||
resolveTypeAnnotation, | ||
createParserErrorCapturer, | ||
getTypes, | ||
}; |
@@ -9,9 +9,16 @@ declare type WritablePropType<T> = T extends ReadonlyArray<infer E1> ? WritableObjectType<E1>[] : T extends (infer E2)[] ? WritableObjectType<E2>[] : WritableObjectType<T>; | ||
} | ||
export interface RNRawObjectProperty { | ||
name: string; | ||
optional: boolean; | ||
propertyType: RNRawType; | ||
} | ||
export interface RNRawObjectType { | ||
kind: 'Object'; | ||
properties: { | ||
name: string; | ||
propertyType: RNRawType; | ||
}[]; | ||
properties: RNRawObjectProperty[]; | ||
} | ||
export interface RNRawFunctionParameter { | ||
name: string; | ||
optional: boolean; | ||
parameterType: RNRawType; | ||
} | ||
export declare type RNRawType = ({ | ||
@@ -26,3 +33,3 @@ kind: 'StringLiterals'; | ||
} | { | ||
kind: 'rn:ColorPrimitive' | 'rn:ImageSourcePrimitive' | 'rn:PointPrimitive' | 'rn:EdgeInsetsPrimitive'; | ||
kind: 'rn:ColorPrimitive' | 'rn:ImageSourcePrimitive' | 'rn:PointPrimitive' | 'rn:EdgeInsetsPrimitive' | 'rn:RootTag'; | ||
} | { | ||
@@ -43,10 +50,10 @@ kind: 'Array'; | ||
returnType: RNRawType; | ||
parameters: { | ||
name: string; | ||
parameterType: RNRawType; | ||
}[]; | ||
parameters: RNRawFunctionParameter[]; | ||
} | { | ||
kind: 'Union' | 'Tuple'; | ||
types: RNRawType[]; | ||
} | { | ||
kind: 'Alias'; | ||
name: string; | ||
}) & RNRawTypeCommon; | ||
export {}; |
import * as ts from 'typescript'; | ||
import { RNRawType } from './RNRawType'; | ||
export declare function typeToRNRawType(typeNode: ts.TypeNode, sourceFile: ts.SourceFile, allowObject: boolean): RNRawType; | ||
export interface RNRawTypeOptions { | ||
allowObject: boolean; | ||
knownAliases?: string[]; | ||
} | ||
export declare function typeToRNRawType(typeNode: ts.TypeNode, sourceFile: ts.SourceFile, options: RNRawTypeOptions): RNRawType; |
@@ -5,6 +5,7 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.typeToRNRawType = void 0; | ||
// tslint:disable:no-conditional-assignment | ||
var ts = require("typescript"); | ||
var ExportParser_1 = require("./ExportParser"); | ||
function functionToRNRawType(typeNode, sourceFile, allowObject) { | ||
function functionToRNRawType(typeNode, sourceFile, options) { | ||
if (typeNode.typeParameters !== undefined && typeNode.typeParameters.length !== 0) { | ||
@@ -18,12 +19,13 @@ throw new Error("Type is not supported: " + typeNode.getText() + "."); | ||
? { kind: 'Void', isNullable: false } | ||
: typeToRNRawType(typeNode.type, sourceFile, allowObject), | ||
: typeToRNRawType(typeNode.type, sourceFile, options), | ||
parameters: typeNode.parameters.map(function (decl) { return ({ | ||
name: decl.name.getText(), | ||
optional: decl.questionToken !== undefined, | ||
parameterType: decl.type === undefined | ||
? { kind: 'Any', isNullable: false } | ||
: typeToRNRawType(decl.type, sourceFile, allowObject) | ||
: typeToRNRawType(decl.type, sourceFile, options) | ||
}); }) | ||
}; | ||
} | ||
function typeToRNRawType(typeNode, sourceFile, allowObject) { | ||
function typeToRNRawType(typeNode, sourceFile, options) { | ||
var itemNullable = false; | ||
@@ -72,2 +74,5 @@ var itemDefaultValue; | ||
break; | ||
case 'RootTag': | ||
itemOthers.push({ kind: 'rn:RootTag', isNullable: false }); | ||
break; | ||
case 'Object': | ||
@@ -80,3 +85,3 @@ itemOthers.push({ kind: 'js:Object', isNullable: false }); | ||
} | ||
itemOthers.push({ kind: 'js:Promise', isNullable: false, elementType: typeToRNRawType(item.typeArguments[0], sourceFile, allowObject) }); | ||
itemOthers.push({ kind: 'js:Promise', isNullable: false, elementType: typeToRNRawType(item.typeArguments[0], sourceFile, options) }); | ||
break; | ||
@@ -89,3 +94,3 @@ } | ||
} | ||
var eventType = typeToRNRawType(item.typeArguments[0], sourceFile, true); | ||
var eventType = typeToRNRawType(item.typeArguments[0], sourceFile, { allowObject: true, knownAliases: options.knownAliases }); | ||
var nameType = void 0; | ||
@@ -114,3 +119,3 @@ if (item.typeArguments.length === 2) { | ||
itemNullable = true; | ||
itemOthers.push(typeToRNRawType(item.typeArguments[0], sourceFile, allowObject)); | ||
itemOthers.push(typeToRNRawType(item.typeArguments[0], sourceFile, options)); | ||
var defaultValue = item.typeArguments[1]; | ||
@@ -168,3 +173,3 @@ switch (defaultValue.kind) { | ||
} | ||
itemOthers.push(typeToRNRawType(item.typeArguments[0], sourceFile, allowObject)); | ||
itemOthers.push(typeToRNRawType(item.typeArguments[0], sourceFile, options)); | ||
break; | ||
@@ -177,12 +182,17 @@ } | ||
} | ||
itemOthers.push({ kind: 'Array', isNullable: false, elementType: typeToRNRawType(item.typeArguments[0], sourceFile, allowObject) }); | ||
itemOthers.push({ kind: 'Array', isNullable: false, elementType: typeToRNRawType(item.typeArguments[0], sourceFile, options) }); | ||
break; | ||
} | ||
default: { | ||
var resolvedType = ExportParser_1.resolveType(item, sourceFile); | ||
if (resolvedType !== item) { | ||
scannedItems.push(resolvedType); | ||
if (options.knownAliases !== undefined && options.knownAliases.indexOf(typeReferenceName) !== -1) { | ||
itemOthers.push({ kind: 'Alias', isNullable: false, name: typeReferenceName }); | ||
} | ||
else { | ||
recognized = false; | ||
var resolvedType = ExportParser_1.resolveType(item, sourceFile); | ||
if (resolvedType !== item) { | ||
scannedItems.push(resolvedType); | ||
} | ||
else { | ||
recognized = false; | ||
} | ||
} | ||
@@ -202,3 +212,3 @@ } | ||
else if (ts.isArrayTypeNode(item)) { | ||
itemOthers.push({ kind: 'Array', isNullable: false, elementType: typeToRNRawType(item.elementType, sourceFile, allowObject) }); | ||
itemOthers.push({ kind: 'Array', isNullable: false, elementType: typeToRNRawType(item.elementType, sourceFile, options) }); | ||
} | ||
@@ -215,2 +225,5 @@ else if (ts.isLiteralTypeNode(item)) { | ||
} | ||
else if (item.literal.kind === ts.SyntaxKind.NullKeyword) { | ||
itemNullable = true; | ||
} | ||
else { | ||
@@ -221,3 +234,3 @@ throw new Error("Type is not supported: " + typeNode.getText() + ", because " + item.literal + " is not a valid literal type."); | ||
else if (ts.isFunctionTypeNode(item)) { | ||
itemOthers.push(functionToRNRawType(item, sourceFile, allowObject)); | ||
itemOthers.push(functionToRNRawType(item, sourceFile, options)); | ||
} | ||
@@ -228,3 +241,3 @@ else if (ts.isTupleTypeNode(item)) { | ||
isNullable: false, | ||
types: item.elementTypes.map(function (elementType) { return typeToRNRawType(elementType, sourceFile, allowObject); }) | ||
types: item.elements.map(function (elementType) { return typeToRNRawType(elementType, sourceFile, options); }) | ||
}); | ||
@@ -258,3 +271,3 @@ } | ||
if (!recognized) { | ||
if (allowObject) { | ||
if (options.allowObject) { | ||
var members = ExportParser_1.getMembersFromType(item, sourceFile); | ||
@@ -275,3 +288,3 @@ if (members === undefined) { | ||
if (ts.isMethodSignature(member) || ts.isCallSignatureDeclaration(member)) { | ||
propertyType = functionToRNRawType(member, sourceFile, allowObject); | ||
propertyType = functionToRNRawType(member, sourceFile, options); | ||
} | ||
@@ -281,10 +294,8 @@ else if (ts.isPropertySignature(member) || ts.isPropertyDeclaration(member)) { | ||
? { kind: 'Any', isNullable: false } | ||
: typeToRNRawType(member.type, sourceFile, allowObject); | ||
: typeToRNRawType(member.type, sourceFile, options); | ||
} | ||
if (propertyType !== undefined) { | ||
if (member.questionToken !== undefined) { | ||
propertyType.isNullable = true; | ||
} | ||
rawObjectType.properties.push({ | ||
name: name, | ||
optional: member.questionToken !== undefined, | ||
propertyType: propertyType | ||
@@ -291,0 +302,0 @@ }); |
{ | ||
"name": "react-native-tscodegen", | ||
"version": "0.66.1", | ||
"version": "0.67.0", | ||
"description": "TypeScript Code Generation for React Native Turbo Module", | ||
@@ -28,3 +28,3 @@ "main": "./lib/index.js", | ||
"dependencies": { | ||
"typescript": "^3.5.3", | ||
"typescript": "^4.1.5", | ||
"jscodeshift": "0.6.4", | ||
@@ -31,0 +31,0 @@ "nullthrows": "1.1.1" |
@@ -45,4 +45,6 @@ # Welcome to react-native-tscodegen (alpha)! | ||
"tests", | ||
"shadow-nodes", | ||
"modules" | ||
"shadow-nodes", | ||
"modulesAndroid" | ||
"modulesCxx" | ||
"modulesIOS" | ||
], | ||
@@ -49,0 +51,0 @@ "inputFile": "./src/turboModule.ts" |
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
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
376111
76
10221
69
5
+ Addedtypescript@4.9.5(transitive)
- Removedtypescript@3.9.10(transitive)
Updatedtypescript@^4.1.5