Rewrite GenerateViewConfigJs with @babel/core

This commit is contained in:
Phil Pluckthun
2025-02-24 14:32:16 +00:00
parent a8dc575114
commit c3de4988c2
@@ -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<string, string>;
@@ -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<any>, 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');
})