Generate ThirdpartyFabricComponentsProvider to extend RCTFabricComponentsPlugins

Summary:
Currently, we don't have a way to extend RCTFabricComponentsPlugins in OSS. This diff  adds a codegen to generate ThirdpartyFabricComponentsProviderwhich will be used to extend RCTFabricComponentsPlugins.

It works almost exactly the same as RCTFabricComponentsPlugins, and in the future we might want to consider merging them together.

Changelog: [internal]

Reviewed By: hramos

Differential Revision: D32128760

fbshipit-source-id: b4f3a082f94c3053251cc6a0323c488f649deaa9
This commit is contained in:
Sota Ogo
2021-11-10 10:09:59 -08:00
committed by Facebook GitHub Bot
parent b27a83b084
commit d065b06da1
7 changed files with 454 additions and 9 deletions
+59 -9
View File
@@ -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<Generators>,
type SchemasGenerators = 'providerIOS';
type LibraryConfig = $ReadOnly<{
generators: Array<LibraryGenerators>,
test?: boolean,
}>;
const GENERATORS = {
type SchemasConfig = $ReadOnly<{
generators: Array<SchemasGenerators>,
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<string, string>, 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
@@ -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<string, string>;
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 <React/RCTComponentViewProtocol.h>
#ifdef __cplusplus
extern "C" {
#endif
Class<RCTComponentViewProtocol> RCTThirdPartyFabricComponentsProvider(const char *name);
::_LOOKUP_FUNCS_::
#ifdef __cplusplus
}
#endif
#pragma GCC diagnostic pop
`;
const lookupFuncTemplate = `
Class<RCTComponentViewProtocol> ::_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]]);
},
};
@@ -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<string, string>;
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 <string>
#import <unordered_map>
Class<RCTComponentViewProtocol> RCTThirdPartyFabricComponentsProvider(const char *name) {
static std::unordered_map<std::string, Class (*)(void)> 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]]);
},
};
@@ -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();
});
});
@@ -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();
});
});
@@ -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 <React/RCTComponentViewProtocol.h>
#ifdef __cplusplus
extern \\"C\\" {
#endif
Class<RCTComponentViewProtocol> RCTThirdPartyFabricComponentsProvider(const char *name);
Class<RCTComponentViewProtocol> NoPropsNoEventsComponentCls(void) __attribute__((used)); // NO_PROPS_NO_EVENTS
Class<RCTComponentViewProtocol> BooleanPropNativeComponentCls(void) __attribute__((used)); // BOOLEAN_PROP
Class<RCTComponentViewProtocol> StringPropComponentCls(void) __attribute__((used)); // STRING_PROP
Class<RCTComponentViewProtocol> IntegerPropNativeComponentCls(void) __attribute__((used)); // INTEGER_PROPS
Class<RCTComponentViewProtocol> DoublePropNativeComponentCls(void) __attribute__((used)); // DOUBLE_PROPS
Class<RCTComponentViewProtocol> FloatPropNativeComponentCls(void) __attribute__((used)); // FLOAT_PROPS
Class<RCTComponentViewProtocol> ColorPropNativeComponentCls(void) __attribute__((used)); // COLOR_PROP
Class<RCTComponentViewProtocol> ImagePropNativeComponentCls(void) __attribute__((used)); // IMAGE_PROP
Class<RCTComponentViewProtocol> PointPropNativeComponentCls(void) __attribute__((used)); // POINT_PROP
Class<RCTComponentViewProtocol> InsetsPropNativeComponentCls(void) __attribute__((used)); // INSETS_PROP
Class<RCTComponentViewProtocol> ArrayPropsNativeComponentCls(void) __attribute__((used)); // ARRAY_PROPS
Class<RCTComponentViewProtocol> ArrayPropsNativeComponentCls(void) __attribute__((used)); // ARRAY_PROPS_WITH_NESTED_OBJECT
Class<RCTComponentViewProtocol> ObjectPropsCls(void) __attribute__((used)); // OBJECT_PROPS
Class<RCTComponentViewProtocol> ImageColorPropNativeComponentCls(void) __attribute__((used)); // MULTI_NATIVE_PROP
Class<RCTComponentViewProtocol> StringEnumPropsNativeComponentCls(void) __attribute__((used)); // STRING_ENUM_PROP
Class<RCTComponentViewProtocol> Int32EnumPropsNativeComponentCls(void) __attribute__((used)); // INT32_ENUM_PROP
Class<RCTComponentViewProtocol> EventsNativeComponentCls(void) __attribute__((used)); // EVENT_PROPS
Class<RCTComponentViewProtocol> EventsNestedObjectNativeComponentCls(void) __attribute__((used)); // EVENT_NESTED_OBJECT_PROPS
Class<RCTComponentViewProtocol> MultiComponent1NativeComponentCls(void) __attribute__((used)); // TWO_COMPONENTS_SAME_FILE
Class<RCTComponentViewProtocol> MultiComponent2NativeComponentCls(void) __attribute__((used)); // TWO_COMPONENTS_SAME_FILE
Class<RCTComponentViewProtocol> MultiFile1NativeComponentCls(void) __attribute__((used)); // TWO_COMPONENTS_DIFFERENT_FILES
Class<RCTComponentViewProtocol> MultiFile2NativeComponentCls(void) __attribute__((used)); // TWO_COMPONENTS_DIFFERENT_FILES
Class<RCTComponentViewProtocol> CommandNativeComponentCls(void) __attribute__((used)); // COMMANDS
Class<RCTComponentViewProtocol> CommandNativeComponentCls(void) __attribute__((used)); // COMMANDS_AND_PROPS
Class<RCTComponentViewProtocol> ExcludedAndroidComponentCls(void) __attribute__((used)); // EXCLUDE_ANDROID
#ifdef __cplusplus
}
#endif
#pragma GCC diagnostic pop
",
}
`;
@@ -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 <string>
#import <unordered_map>
Class<RCTComponentViewProtocol> RCTThirdPartyFabricComponentsProvider(const char *name) {
static std::unordered_map<std::string, Class (*)(void)> 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;
}
",
}
`;