From c3de4988c2a8fbfc490070ddcf4516cee831d673 Mon Sep 17 00:00:00 2001 From: Phil Pluckthun Date: Mon, 24 Feb 2025 14:27:45 +0000 Subject: [PATCH] Rewrite GenerateViewConfigJs with @babel/core --- .../components/GenerateViewConfigJs.js | 234 ++++++++---------- 1 file changed, 99 insertions(+), 135 deletions(-) diff --git a/packages/react-native-codegen/src/generators/components/GenerateViewConfigJs.js b/packages/react-native-codegen/src/generators/components/GenerateViewConfigJs.js index 91cbc056423..e2042660e68 100644 --- a/packages/react-native-codegen/src/generators/components/GenerateViewConfigJs.js +++ b/packages/react-native-codegen/src/generators/components/GenerateViewConfigJs.js @@ -16,7 +16,8 @@ import type { } from '../../CodegenSchema'; import type {SchemaType} from '../../CodegenSchema'; -const j = require('jscodeshift'); +const core = require('@babel/core'); +const t = core.types; // File path -> contents type FilesOutput = Map; @@ -50,6 +51,10 @@ ${componentConfig} // this multiple times. const UIMANAGER_IMPORT = 'const {UIManager} = require("react-native")'; +function expression(input: string) { + return core.template.expression(input)(); +} + function getReactDiffProcessValue(typeAnnotation: PropTypeAnnotation) { switch (typeAnnotation.type) { case 'BooleanTypeAnnotation': @@ -61,25 +66,21 @@ function getReactDiffProcessValue(typeAnnotation: PropTypeAnnotation) { case 'StringEnumTypeAnnotation': case 'Int32EnumTypeAnnotation': case 'MixedTypeAnnotation': - return j.literal(true); + return t.booleanLiteral(true); case 'ReservedPropTypeAnnotation': switch (typeAnnotation.name) { case 'ColorPrimitive': - return j.template - .expression`{ process: require('react-native/Libraries/StyleSheet/processColor').default }`; + return expression(`{ process: require('react-native/Libraries/StyleSheet/processColor').default }`); case 'ImageSourcePrimitive': - return j.template - .expression`{ process: require('react-native/Libraries/Image/resolveAssetSource') }`; + return expression(`{ process: require('react-native/Libraries/Image/resolveAssetSource') }`); case 'ImageRequestPrimitive': throw new Error('ImageRequest should not be used in props'); case 'PointPrimitive': - return j.template - .expression`{ diff: ((req) => 'default' in req ? req.default : req)(require('react-native/Libraries/Utilities/differ/pointsDiffer')) }`; + return expression(`{ diff: ((req) => 'default' in req ? req.default : req)(require('react-native/Libraries/Utilities/differ/pointsDiffer')) }`); case 'EdgeInsetsPrimitive': - return j.template - .expression`{ diff: ((req) => 'default' in req ? req.default : req)(require('react-native/Libraries/Utilities/differ/insetsDiffer')) }`; + return expression(`{ diff: ((req) => 'default' in req ? req.default : req)(require('react-native/Libraries/Utilities/differ/insetsDiffer')) }`); case 'DimensionPrimitive': - return j.literal(true); + return t.booleanLiteral(true); default: (typeAnnotation.name: empty); throw new Error( @@ -90,20 +91,19 @@ function getReactDiffProcessValue(typeAnnotation: PropTypeAnnotation) { if (typeAnnotation.elementType.type === 'ReservedPropTypeAnnotation') { switch (typeAnnotation.elementType.name) { case 'ColorPrimitive': - return j.template - .expression`{ process: ((req) => 'default' in req ? req.default : req)(require('react-native/Libraries/StyleSheet/processColorArray')) }`; + return expression(`{ process: ((req) => 'default' in req ? req.default : req)(require('react-native/Libraries/StyleSheet/processColorArray')) }`); case 'ImageSourcePrimitive': case 'PointPrimitive': case 'EdgeInsetsPrimitive': case 'DimensionPrimitive': - return j.literal(true); + return t.booleanLiteral(true); default: throw new Error( `Received unknown array native typeAnnotation: "${typeAnnotation.elementType.name}"`, ); } } - return j.literal(true); + return t.booleanLiteral(true); default: (typeAnnotation: empty); throw new Error( @@ -134,7 +134,7 @@ ${ : '' } -export const __INTERNAL_VIEW_CONFIG = VIEW_CONFIG; +export const __INTERNAL_VIEW_CONFIG = %%VIEW_CONFIG%%; export default NativeComponentRegistry.get(nativeComponentName, () => __INTERNAL_VIEW_CONFIG); `.trim(); @@ -180,36 +180,40 @@ function getValidAttributesForEvents( "const {ConditionallyIgnoredEventHandlers} = require('react-native/Libraries/NativeComponent/ViewConfigIgnore');", ); - const validAttributes = j.objectExpression( + const validAttributes = t.objectExpression( events.map(eventType => { - return j.property('init', j.identifier(eventType.name), j.literal(true)); + return t.objectProperty( + t.identifier(eventType.name), + t.booleanLiteral(true), + ); }), ); - return j.callExpression(j.identifier('ConditionallyIgnoredEventHandlers'), [ - validAttributes, - ]); + return t.callExpression( + t.identifier('ConditionallyIgnoredEventHandlers'), + [validAttributes] + ); } function generateBubblingEventInfo( event: EventTypeShape, nameOveride: void | string, ) { - return j.property( - 'init', - j.identifier(normalizeInputEventName(nameOveride || event.name)), - j.objectExpression([ - j.property( - 'init', - j.identifier('phasedRegistrationNames'), - j.objectExpression([ - j.property( - 'init', - j.identifier('captured'), - j.literal(`${event.name}Capture`), + return t.objectProperty( + t.identifier(normalizeInputEventName(nameOveride || event.name)), + t.objectExpression([ + t.objectProperty( + t.identifier('phasedRegistrationNames'), + t.objectExpression([ + t.objectProperty( + t.identifier('captured'), + t.identifier(`${event.name}Capture`), ), - j.property('init', j.identifier('bubbled'), j.literal(event.name)), - ]), + t.objectProperty( + t.identifier('bubbled'), + t.identifier(event.name), + ), + ]) ), ]), ); @@ -219,14 +223,12 @@ function generateDirectEventInfo( event: EventTypeShape, nameOveride: void | string, ) { - return j.property( - 'init', - j.identifier(normalizeInputEventName(nameOveride || event.name)), - j.objectExpression([ - j.property( - 'init', - j.identifier('registrationName'), - j.literal(event.name), + return t.objectProperty( + t.identifier(normalizeInputEventName(nameOveride || event.name)), + t.objectExpression([ + t.objectProperty( + t.identifier('registrationName'), + t.identifier(event.name), ), ]), ); @@ -261,17 +263,16 @@ function buildViewConfig( } }); - const validAttributes = j.objectExpression([ + const validAttributes = t.objectExpression([ ...componentProps.map(schemaProp => { - return j.property( - 'init', - j.identifier(schemaProp.name), + return t.objectProperty( + t.identifier(schemaProp.name), getReactDiffProcessValue(schemaProp.typeAnnotation), ); }), ...(componentEvents.length > 0 ? [ - j.spreadProperty( + t.spreadElement( getValidAttributesForEvents(componentEvents, imports), ), ] @@ -294,15 +295,6 @@ function buildViewConfig( return bubblingEvents; }, []); - const bubblingEvents = - bubblingEventNames.length > 0 - ? j.property( - 'init', - j.identifier('bubblingEventTypes'), - j.objectExpression(bubblingEventNames), - ) - : null; - const directEventNames = component.events .filter(event => event.bubblingType === 'direct') .reduce((directEvents: Array, event) => { @@ -319,27 +311,28 @@ function buildViewConfig( return directEvents; }, []); - const directEvents = - directEventNames.length > 0 - ? j.property( - 'init', - j.identifier('directEventTypes'), - j.objectExpression(directEventNames), - ) - : null; - const properties = [ - j.property( - 'init', - j.identifier('uiViewClassName'), - j.literal(componentName), + t.objectProperty( + t.identifier('uiViewClassName'), + t.stringLiteral(componentName), ), - bubblingEvents, - directEvents, - j.property('init', j.identifier('validAttributes'), validAttributes), - ].filter(Boolean); + ]; - return j.objectExpression(properties); + if (bubblingEventNames.length > 0) { + properties.push(t.objectProperty( + t.identifier('bubblingEventTypes'), + t.objectExpression(bubblingEventNames), + )); + } + if (directEventNames.length > 0) { + properties.push(t.objectProperty( + t.identifier('directEventTypes'), + t.objectExpression(directEventNames), + )); + } + + properties.push(t.objectProperty(t.identifier('validAttributes'), validAttributes)); + return t.objectExpression(properties); } function buildCommands( @@ -358,44 +351,32 @@ function buildCommands( 'const {dispatchCommand} = require("react-native/Libraries/ReactNative/RendererProxy");', ); - const properties = commands.map(command => { - const commandName = command.name; - const params = command.typeAnnotation.params; + const commandsObject = t.objectExpression( + commands.map(command => { + const commandName = command.name; + const params = command.typeAnnotation.params; - const commandNameLiteral = j.literal(commandName); - const commandNameIdentifier = j.identifier(commandName); - const arrayParams = j.arrayExpression( - params.map(param => { - return j.identifier(param.name); - }), - ); + const dispatchCommandCall = t.callExpression(t.identifier('dispatchCommand'), [ + t.identifier(commandName), + t.arrayExpression(params.map(param => t.identifier(param.name))), + ]); - const expression = j.template - .expression`dispatchCommand(ref, ${commandNameLiteral}, ${arrayParams})`; + return t.objectMethod( + 'method', + t.identifier(commandName), + [t.identifier('ref'), ...params.map(param => t.identifier(param.name))], + t.blockStatement([ + t.expressionStatement(dispatchCommandCall), + ]), + ); + }) + ); - const functionParams = params.map(param => { - return j.identifier(param.name); - }); - - const property = j.property( - 'init', - commandNameIdentifier, - j.functionExpression( - null, - [j.identifier('ref'), ...functionParams], - j.blockStatement([j.expressionStatement(expression)]), - ), - ); - property.method = true; - - return property; - }); - - return j.exportNamedDeclaration( - j.variableDeclaration('const', [ - j.variableDeclarator( - j.identifier('Commands'), - j.objectExpression(properties), + return t.exportNamedDeclaration( + t.variableDeclaration('const', [ + t.variableDeclarator( + t.identifier('Commands'), + commandsObject, ), ]), ); @@ -431,42 +412,25 @@ module.exports = { component.paperComponentNameDeprecated, }); - const replacedSourceRoot = j.withParser('flow')(replacedTemplate); - const paperComponentName = component.paperComponentName ?? componentName; - replacedSourceRoot - .find(j.Identifier, { - name: 'VIEW_CONFIG', - }) - .replaceWith( - buildViewConfig( - schema, - paperComponentName, - component, - imports, - ), - ); + const replacedSourceRoot = core.template.program(replacedTemplate)({ + VIEW_CONFIG: buildViewConfig(schema, paperComponentName, component, imports), + }); - const commands = buildCommands( + const commandsExport = buildCommands( schema, paperComponentName, component, imports, ); - if (commands) { - replacedSourceRoot - .find(j.ExportDefaultDeclaration) - .insertAfter(j(commands).toSource()); + if (commandsExport) { + replacedSourceRoot.body.push(commandsExport); } - const replacedSource: string = replacedSourceRoot.toSource({ - quote: 'single', - trailingComma: true, - }); - - return replacedSource; + const replacedSource = core.transformFromAstSync(replacedSourceRoot); + return replacedSource.code; }) .join('\n\n'); })