diff --git a/packages/react-native-codegen/src/generators/RNCodegen.js b/packages/react-native-codegen/src/generators/RNCodegen.js index 1343c598c7d..b033d13e4eb 100644 --- a/packages/react-native-codegen/src/generators/RNCodegen.js +++ b/packages/react-native-codegen/src/generators/RNCodegen.js @@ -34,13 +34,15 @@ const generatePropsJavaDelegate = require('./components/GeneratePropsJavaDelegat const generateTests = require('./components/GenerateTests.js'); const generateShadowNodeCpp = require('./components/GenerateShadowNodeCpp.js'); const generateShadowNodeH = require('./components/GenerateShadowNodeH.js'); +const generateThirdPartyFabricComponentsProviderObjCpp = require('./components/GenerateThirdPartyFabricComponentsProviderObjCpp.js'); +const generateThirdPartyFabricComponentsProviderH = require('./components/GenerateThirdPartyFabricComponentsProviderH.js'); const generateViewConfigJs = require('./components/GenerateViewConfigJs.js'); const path = require('path'); const schemaValidator = require('../SchemaValidator.js'); import type {SchemaType} from '../CodegenSchema'; -type Options = $ReadOnly<{ +type LibraryOptions = $ReadOnly<{ libraryName: string, schema: SchemaType, outputDirectory: string, @@ -48,7 +50,12 @@ type Options = $ReadOnly<{ assumeNonnull: boolean, }>; -type Generators = +type SchemasOptions = $ReadOnly<{ + schemas: {[string]: SchemaType}, + outputDirectory: string, +}>; + +type LibraryGenerators = | 'componentsAndroid' | 'componentsIOS' | 'descriptors' @@ -60,12 +67,19 @@ type Generators = | 'modulesCxx' | 'modulesIOS'; -type Config = $ReadOnly<{ - generators: Array, +type SchemasGenerators = 'providerIOS'; + +type LibraryConfig = $ReadOnly<{ + generators: Array, test?: boolean, }>; -const GENERATORS = { +type SchemasConfig = $ReadOnly<{ + generators: Array, + test?: boolean, +}>; + +const LIBRARY_GENERATORS = { descriptors: [generateComponentDescriptorH.generate], events: [generateEventEmitterCpp.generate, generateEventEmitterH.generate], props: [ @@ -113,6 +127,13 @@ const GENERATORS = { ], }; +const SCHEMAS_GENERATORS = { + providerIOS: [ + generateThirdPartyFabricComponentsProviderObjCpp.generate, + generateThirdPartyFabricComponentsProviderH.generate, + ], +}; + function writeMapToFiles(map: Map, outputDir: string) { let success = true; map.forEach((contents: string, fileName: string) => { @@ -153,14 +174,20 @@ function checkFilesForChanges( module.exports = { generate( - {libraryName, schema, outputDirectory, packageName, assumeNonnull}: Options, - {generators, test}: Config, + { + libraryName, + schema, + outputDirectory, + packageName, + assumeNonnull, + }: LibraryOptions, + {generators, test}: LibraryConfig, ): boolean { schemaValidator.validate(schema); const generatedFiles = []; for (const name of generators) { - for (const generator of GENERATORS[name]) { + for (const generator of LIBRARY_GENERATORS[name]) { generatedFiles.push( ...generator(libraryName, schema, packageName, assumeNonnull), ); @@ -175,7 +202,30 @@ module.exports = { return writeMapToFiles(filesToUpdate, outputDirectory); }, - generateViewConfig({libraryName, schema}: Options): string { + generateFromSchemas( + {schemas, outputDirectory}: SchemasOptions, + {generators, test}: SchemasConfig, + ): boolean { + Object.keys(schemas).forEach(libraryName => + schemaValidator.validate(schemas[libraryName]), + ); + + const generatedFiles = []; + for (const name of generators) { + for (const generator of SCHEMAS_GENERATORS[name]) { + generatedFiles.push(...generator(schemas)); + } + } + + const filesToUpdate = new Map([...generatedFiles]); + + if (test === true) { + return checkFilesForChanges(filesToUpdate, outputDirectory); + } + + return writeMapToFiles(filesToUpdate, outputDirectory); + }, + generateViewConfig({libraryName, schema}: LibraryOptions): string { schemaValidator.validate(schema); const result = generateViewConfigJs diff --git a/packages/react-native-codegen/src/generators/components/GenerateThirdPartyFabricComponentsProviderH.js b/packages/react-native-codegen/src/generators/components/GenerateThirdPartyFabricComponentsProviderH.js new file mode 100644 index 00000000000..2dd0caf3ee8 --- /dev/null +++ b/packages/react-native-codegen/src/generators/components/GenerateThirdPartyFabricComponentsProviderH.js @@ -0,0 +1,105 @@ +/** + * Copyright (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. + * + * @flow strict + * @format + */ + +'use strict'; + +import type {SchemaType} from '../../CodegenSchema'; + +// File path -> contents +type FilesOutput = Map; + +const template = ` +/* + * ${'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 GenerateRCTThirdPartyFabricComponentsProviderH + */ + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wreturn-type-c-linkage" + +#import + +#ifdef __cplusplus +extern "C" { +#endif + +Class RCTThirdPartyFabricComponentsProvider(const char *name); + +::_LOOKUP_FUNCS_:: + +#ifdef __cplusplus +} +#endif + +#pragma GCC diagnostic pop + +`; + +const lookupFuncTemplate = ` +Class ::_CLASSNAME_::Cls(void) __attribute__((used)); // ::_LIBRARY_NAME_:: +`.trim(); + +module.exports = { + generate(schemas: {[string]: SchemaType}): FilesOutput { + const fileName = 'RCTThirdPartyFabricComponentsProvider.h'; + + const lookupFuncs = Object.keys(schemas) + .map(libraryName => { + const schema = schemas[libraryName]; + return Object.keys(schema.modules) + .map(moduleName => { + const module = schema.modules[moduleName]; + if (module.type !== 'Component') { + return; + } + + const {components} = module; + // No components in this module + if (components == null) { + return null; + } + + return Object.keys(components) + .filter(componentName => { + const component = components[componentName]; + return !( + component.excludedPlatforms && + component.excludedPlatforms.includes('iOS') + ); + }) + .map(componentName => { + const component = components[componentName]; + if (component.interfaceOnly === true) { + return; + } + + return lookupFuncTemplate + .replace(/::_LIBRARY_NAME_::/g, libraryName) + .replace(/::_CLASSNAME_::/g, componentName); + }) + .join('\n'); + }) + .filter(Boolean) + .join('\n'); + }) + .join('\n'); + + const replacedTemplate = template.replace( + /::_LOOKUP_FUNCS_::/g, + lookupFuncs, + ); + + return new Map([[fileName, replacedTemplate]]); + }, +}; diff --git a/packages/react-native-codegen/src/generators/components/GenerateThirdPartyFabricComponentsProviderObjCpp.js b/packages/react-native-codegen/src/generators/components/GenerateThirdPartyFabricComponentsProviderObjCpp.js new file mode 100644 index 00000000000..b2a10acba1f --- /dev/null +++ b/packages/react-native-codegen/src/generators/components/GenerateThirdPartyFabricComponentsProviderObjCpp.js @@ -0,0 +1,99 @@ +/** + * Copyright (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. + * + * @flow strict + * @format + */ + +'use strict'; + +import type {SchemaType} from '../../CodegenSchema'; + +// File path -> contents +type FilesOutput = Map; + +const template = ` +/** + * ${'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 GenerateRCTThirdPartyFabricComponentsProviderCpp + */ + +// OSS-compatibility layer + +#import "RCTThirdPartyFabricComponentsProvider.h" + +#import +#import + +Class RCTThirdPartyFabricComponentsProvider(const char *name) { + static std::unordered_map sFabricComponentsClassMap = { +::_LOOKUP_MAP_:: + }; + + auto p = sFabricComponentsClassMap.find(name); + if (p != sFabricComponentsClassMap.end()) { + auto classFunc = p->second; + return classFunc(); + } + return nil; +} +`; + +const lookupMapTemplate = ` + {"::_CLASSNAME_::", ::_CLASSNAME_::Cls}, // ::_LIBRARY_NAME_::`; + +module.exports = { + generate(schemas: {[string]: SchemaType}): FilesOutput { + const fileName = 'RCTThirdPartyFabricComponentsProvider.mm'; + + const lookupMap = Object.keys(schemas) + .map(libraryName => { + const schema = schemas[libraryName]; + return Object.keys(schema.modules) + .map(moduleName => { + const module = schema.modules[moduleName]; + if (module.type !== 'Component') { + return; + } + + const {components} = module; + // No components in this module + if (components == null) { + return null; + } + + return Object.keys(components) + .filter(componentName => { + const component = components[componentName]; + return !( + component.excludedPlatforms && + component.excludedPlatforms.includes('iOS') + ); + }) + .map(componentName => { + if (components[componentName].interfaceOnly === true) { + return; + } + const replacedTemplate = lookupMapTemplate + .replace(/::_CLASSNAME_::/g, componentName) + .replace(/::_LIBRARY_NAME_::/g, libraryName); + + return replacedTemplate; + }); + }) + .filter(Boolean); + }) + .join('\n'); + + const replacedTemplate = template.replace(/::_LOOKUP_MAP_::/g, lookupMap); + + return new Map([[fileName, replacedTemplate]]); + }, +}; diff --git a/packages/react-native-codegen/src/generators/components/__tests__/GenerateThirdPartyFabricComponentsProviderH-test.js b/packages/react-native-codegen/src/generators/components/__tests__/GenerateThirdPartyFabricComponentsProviderH-test.js new file mode 100644 index 00000000000..c3f67d8604c --- /dev/null +++ b/packages/react-native-codegen/src/generators/components/__tests__/GenerateThirdPartyFabricComponentsProviderH-test.js @@ -0,0 +1,21 @@ +/** + * Copyright (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. + * + * @emails oncall+react_native + * @flow strict-local + * @format + */ + +'use strict'; + +const fixtures = require('../__test_fixtures__/fixtures.js'); +const generator = require('../GenerateThirdPartyFabricComponentsProviderH.js'); + +describe('GenerateThirdPartyFabricComponentsProviderH', () => { + it(`can generate fixtures`, () => { + expect(generator.generate(fixtures)).toMatchSnapshot(); + }); +}); diff --git a/packages/react-native-codegen/src/generators/components/__tests__/GenerateThirdPartyFabricComponentsProviderObjCpp-test.js b/packages/react-native-codegen/src/generators/components/__tests__/GenerateThirdPartyFabricComponentsProviderObjCpp-test.js new file mode 100644 index 00000000000..aa70282c0a3 --- /dev/null +++ b/packages/react-native-codegen/src/generators/components/__tests__/GenerateThirdPartyFabricComponentsProviderObjCpp-test.js @@ -0,0 +1,21 @@ +/** + * Copyright (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. + * + * @emails oncall+react_native + * @flow strict-local + * @format + */ + +'use strict'; + +const fixtures = require('../__test_fixtures__/fixtures.js'); +const generator = require('../GenerateThirdPartyFabricComponentsProviderObjCpp.js'); + +describe('GenerateThirdPartyFabricComponentsProviderObjCpp', () => { + it(`can generate fixtures`, () => { + expect(generator.generate(fixtures)).toMatchSnapshot(); + }); +}); diff --git a/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GenerateThirdPartyFabricComponentsProviderH-test.js.snap b/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GenerateThirdPartyFabricComponentsProviderH-test.js.snap new file mode 100644 index 00000000000..223fb9f34cb --- /dev/null +++ b/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GenerateThirdPartyFabricComponentsProviderH-test.js.snap @@ -0,0 +1,63 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`GenerateThirdPartyFabricComponentsProviderH can generate fixtures 1`] = ` +Map { + "RCTThirdPartyFabricComponentsProvider.h" => " +/* + * Copyright (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 GenerateRCTThirdPartyFabricComponentsProviderH + */ + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored \\"-Wreturn-type-c-linkage\\" + +#import + +#ifdef __cplusplus +extern \\"C\\" { +#endif + +Class RCTThirdPartyFabricComponentsProvider(const char *name); + +Class NoPropsNoEventsComponentCls(void) __attribute__((used)); // NO_PROPS_NO_EVENTS + +Class BooleanPropNativeComponentCls(void) __attribute__((used)); // BOOLEAN_PROP +Class StringPropComponentCls(void) __attribute__((used)); // STRING_PROP +Class IntegerPropNativeComponentCls(void) __attribute__((used)); // INTEGER_PROPS +Class DoublePropNativeComponentCls(void) __attribute__((used)); // DOUBLE_PROPS +Class FloatPropNativeComponentCls(void) __attribute__((used)); // FLOAT_PROPS +Class ColorPropNativeComponentCls(void) __attribute__((used)); // COLOR_PROP +Class ImagePropNativeComponentCls(void) __attribute__((used)); // IMAGE_PROP +Class PointPropNativeComponentCls(void) __attribute__((used)); // POINT_PROP +Class InsetsPropNativeComponentCls(void) __attribute__((used)); // INSETS_PROP +Class ArrayPropsNativeComponentCls(void) __attribute__((used)); // ARRAY_PROPS +Class ArrayPropsNativeComponentCls(void) __attribute__((used)); // ARRAY_PROPS_WITH_NESTED_OBJECT +Class ObjectPropsCls(void) __attribute__((used)); // OBJECT_PROPS +Class ImageColorPropNativeComponentCls(void) __attribute__((used)); // MULTI_NATIVE_PROP +Class StringEnumPropsNativeComponentCls(void) __attribute__((used)); // STRING_ENUM_PROP +Class Int32EnumPropsNativeComponentCls(void) __attribute__((used)); // INT32_ENUM_PROP +Class EventsNativeComponentCls(void) __attribute__((used)); // EVENT_PROPS + +Class EventsNestedObjectNativeComponentCls(void) __attribute__((used)); // EVENT_NESTED_OBJECT_PROPS +Class MultiComponent1NativeComponentCls(void) __attribute__((used)); // TWO_COMPONENTS_SAME_FILE +Class MultiComponent2NativeComponentCls(void) __attribute__((used)); // TWO_COMPONENTS_SAME_FILE +Class MultiFile1NativeComponentCls(void) __attribute__((used)); // TWO_COMPONENTS_DIFFERENT_FILES +Class MultiFile2NativeComponentCls(void) __attribute__((used)); // TWO_COMPONENTS_DIFFERENT_FILES +Class CommandNativeComponentCls(void) __attribute__((used)); // COMMANDS +Class CommandNativeComponentCls(void) __attribute__((used)); // COMMANDS_AND_PROPS +Class ExcludedAndroidComponentCls(void) __attribute__((used)); // EXCLUDE_ANDROID + + +#ifdef __cplusplus +} +#endif + +#pragma GCC diagnostic pop + +", +} +`; diff --git a/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GenerateThirdPartyFabricComponentsProviderObjCpp-test.js.snap b/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GenerateThirdPartyFabricComponentsProviderObjCpp-test.js.snap new file mode 100644 index 00000000000..bd0191208ac --- /dev/null +++ b/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GenerateThirdPartyFabricComponentsProviderObjCpp-test.js.snap @@ -0,0 +1,86 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`GenerateThirdPartyFabricComponentsProviderObjCpp can generate fixtures 1`] = ` +Map { + "RCTThirdPartyFabricComponentsProvider.mm" => " +/** + * Copyright (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 GenerateRCTThirdPartyFabricComponentsProviderCpp + */ + +// OSS-compatibility layer + +#import \\"RCTThirdPartyFabricComponentsProvider.h\\" + +#import +#import + +Class RCTThirdPartyFabricComponentsProvider(const char *name) { + static std::unordered_map sFabricComponentsClassMap = { + + {\\"NoPropsNoEventsComponent\\", NoPropsNoEventsComponentCls}, // NO_PROPS_NO_EVENTS + + + {\\"BooleanPropNativeComponent\\", BooleanPropNativeComponentCls}, // BOOLEAN_PROP + + {\\"StringPropComponent\\", StringPropComponentCls}, // STRING_PROP + + {\\"IntegerPropNativeComponent\\", IntegerPropNativeComponentCls}, // INTEGER_PROPS + + {\\"DoublePropNativeComponent\\", DoublePropNativeComponentCls}, // DOUBLE_PROPS + + {\\"FloatPropNativeComponent\\", FloatPropNativeComponentCls}, // FLOAT_PROPS + + {\\"ColorPropNativeComponent\\", ColorPropNativeComponentCls}, // COLOR_PROP + + {\\"ImagePropNativeComponent\\", ImagePropNativeComponentCls}, // IMAGE_PROP + + {\\"PointPropNativeComponent\\", PointPropNativeComponentCls}, // POINT_PROP + + {\\"InsetsPropNativeComponent\\", InsetsPropNativeComponentCls}, // INSETS_PROP + + {\\"ArrayPropsNativeComponent\\", ArrayPropsNativeComponentCls}, // ARRAY_PROPS + + {\\"ArrayPropsNativeComponent\\", ArrayPropsNativeComponentCls}, // ARRAY_PROPS_WITH_NESTED_OBJECT + + {\\"ObjectProps\\", ObjectPropsCls}, // OBJECT_PROPS + + {\\"ImageColorPropNativeComponent\\", ImageColorPropNativeComponentCls}, // MULTI_NATIVE_PROP + + {\\"StringEnumPropsNativeComponent\\", StringEnumPropsNativeComponentCls}, // STRING_ENUM_PROP + + {\\"Int32EnumPropsNativeComponent\\", Int32EnumPropsNativeComponentCls}, // INT32_ENUM_PROP + + {\\"EventsNativeComponent\\", EventsNativeComponentCls}, // EVENT_PROPS + + + {\\"EventsNestedObjectNativeComponent\\", EventsNestedObjectNativeComponentCls}, // EVENT_NESTED_OBJECT_PROPS + + {\\"MultiComponent1NativeComponent\\", MultiComponent1NativeComponentCls}, // TWO_COMPONENTS_SAME_FILE, + {\\"MultiComponent2NativeComponent\\", MultiComponent2NativeComponentCls}, // TWO_COMPONENTS_SAME_FILE + + {\\"MultiFile1NativeComponent\\", MultiFile1NativeComponentCls}, // TWO_COMPONENTS_DIFFERENT_FILES, + {\\"MultiFile2NativeComponent\\", MultiFile2NativeComponentCls}, // TWO_COMPONENTS_DIFFERENT_FILES + + {\\"CommandNativeComponent\\", CommandNativeComponentCls}, // COMMANDS + + {\\"CommandNativeComponent\\", CommandNativeComponentCls}, // COMMANDS_AND_PROPS + + {\\"ExcludedAndroidComponent\\", ExcludedAndroidComponentCls}, // EXCLUDE_ANDROID + + }; + + auto p = sFabricComponentsClassMap.find(name); + if (p != sFabricComponentsClassMap.end()) { + auto classFunc = p->second; + return classFunc(); + } + return nil; +} +", +} +`;