Files
twenty/packages/twenty-sdk/scripts/remote-dom/generators/remote-components.generator.ts
T
Raphaël Bosi cb7d0b83a8 [FRONT COMPONENTS] Twenty UI elements generation (#17866)
## PR description

This PR:
- Creates a TypeScript-based extractor that discovers all exported
twenty-ui components by scanning barrel files, extracting
props/slots/events via ts-morph type analysis, and generating the remote
DOM bindings automatically.

- Adds a new ESLint rule which enforces all *Props types in twenty-ui
components to be exported, which is required for the extractor to
discover component prop types. Existing twenty-ui components are updated
to comply with this rule.

- Extends the remote DOM generation to support slots, per-component
events, forwardRef wrappers, and richer property types (array, object,
function)

## Edge cases to fix in another PR

- Icons cannot be rendered inside buttons
- IconButtons throw an error when mounted
- MenuItems are not displayed correctly
- MenuItemNavigate throws on click

## Video Demo


https://github.com/user-attachments/assets/c2ed67cf-6a15-4896-9fec-e83fac0e862b
2026-02-12 12:48:16 +01:00

66 lines
1.7 KiB
TypeScript

import type { Project, SourceFile } from 'ts-morph';
import { EVENT_TO_REACT } from '@/sdk/front-component-common/EventToReact';
import { type ComponentSchema } from './schemas';
import { addExportedConst, addFileHeader } from './utils';
const generateComponentDefinition = (
sourceFile: SourceFile,
component: ComponentSchema,
): void => {
const hasEvents = component.events.length > 0;
const componentExportName = component.tagName;
let initializer: string;
if (hasEvents) {
const eventProps = component.events
.map((event) => {
const propName = EVENT_TO_REACT[event];
return ` ${propName}: { event: '${event}' },`;
})
.join('\n');
initializer = `createRemoteComponent('${component.customElementName}', ${component.name}Element, {
eventProps: {
${eventProps}
},
})`;
} else {
initializer = `createRemoteComponent('${component.customElementName}', ${component.name}Element)`;
}
addExportedConst(sourceFile, componentExportName, initializer);
};
export const generateRemoteComponents = (
project: Project,
components: ComponentSchema[],
): SourceFile => {
const sourceFile = project.createSourceFile('remote-components.ts', '', {
overwrite: true,
});
sourceFile.addImportDeclaration({
moduleSpecifier: '@remote-dom/react',
namedImports: ['createRemoteComponent'],
});
const elementImports = components.map(
(component) => `${component.name}Element`,
);
sourceFile.addImportDeclaration({
moduleSpecifier: './remote-elements',
namedImports: elementImports,
});
for (const component of components) {
generateComponentDefinition(sourceFile, component);
}
addFileHeader(sourceFile);
return sourceFile;
};