accepts any valid expression, not just an identifier (#22778)

Fixing and issue with front component definition

look at the unit test to see the actual fix
This commit is contained in:
martmull
2026-07-10 15:15:26 +02:00
committed by GitHub
parent 666ceb41d5
commit 945724e016
2 changed files with 113 additions and 21 deletions
@@ -0,0 +1,87 @@
import { unwrapDefineFrontComponentToDirectExport } from '@/cli/utilities/build/common/front-component-build/utils/unwrap-define-front-component-to-direct-export';
const INLINE_EXPRESSION_COMPONENT_SOURCE = `import { defineFrontComponent } from 'twenty-sdk/define';
import { SUMMARIZE_COMPANY_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers';
import { createSummarizeRecordEffect } from 'src/front-components/utils/create-summarize-record-effect';
import { SUMMARIZE_TARGETS } from 'src/front-components/utils/summarize-target';
const SummarizeRecordEffect = createSummarizeRecordEffect(SUMMARIZE_TARGETS.company)
export default defineFrontComponent({
universalIdentifier: SUMMARIZE_COMPANY_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER,
name: 'Summarize Company',
description: 'Asks AI to summarize the selected company',
isHeadless: true,
component: createSummarizeRecordEffect(SUMMARIZE_TARGETS.company),
});
`;
const IDENTIFIER_COMPONENT_SOURCE = `import { defineFrontComponent } from 'twenty-sdk/define';
import { SUMMARIZE_COMPANY_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers';
import { createSummarizeRecordEffect } from 'src/front-components/utils/create-summarize-record-effect';
import { SUMMARIZE_TARGETS } from 'src/front-components/utils/summarize-target';
const SummarizeRecordEffect = createSummarizeRecordEffect(SUMMARIZE_TARGETS.company)
export default defineFrontComponent({
universalIdentifier: SUMMARIZE_COMPANY_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER,
name: 'Summarize Company',
description: 'Asks AI to summarize the selected company',
isHeadless: true,
component: SummarizeRecordEffect,
});
`;
describe('unwrapDefineFrontComponentToDirectExport', () => {
it('renders the component when component is an inline factory call', () => {
const output = unwrapDefineFrontComponentToDirectExport(
INLINE_EXPRESSION_COMPONENT_SOURCE,
);
expect(output).toContain(
'component: createSummarizeRecordEffect(SUMMARIZE_TARGETS.company),',
);
expect(output).toContain(
'__frontComponentJsx(__frontComponentDefinition.component, {})',
);
expect(output).not.toContain('defineFrontComponent');
});
it('renders the component when component is a bare identifier', () => {
const output = unwrapDefineFrontComponentToDirectExport(
IDENTIFIER_COMPONENT_SOURCE,
);
expect(output).toContain('component: SummarizeRecordEffect,');
expect(output).toContain(
'__frontComponentJsx(__frontComponentDefinition.component, {})',
);
expect(output).not.toContain('defineFrontComponent');
});
it('produces an equivalent renderer for the inline and identifier forms', () => {
const inlineOutput = unwrapDefineFrontComponentToDirectExport(
INLINE_EXPRESSION_COMPONENT_SOURCE,
);
const identifierOutput = unwrapDefineFrontComponentToDirectExport(
IDENTIFIER_COMPONENT_SOURCE,
);
const renderer =
'\nexport default function __renderFrontComponent(__container) { __createRoot(__container).render(__frontComponentJsx(__frontComponentDefinition.component, {})); }\n';
expect(inlineOutput).toContain(renderer);
expect(identifierOutput).toContain(renderer);
});
it('leaves a source without a defineFrontComponent default export untouched', () => {
const source = `import { useEffect } from 'react';
export const createSummarizeRecordEffect = () => () => null;
`;
expect(unwrapDefineFrontComponentToDirectExport(source)).toBe(source);
});
});
@@ -1,8 +1,13 @@
const DEFINE_FRONT_COMPONENT_IMPORT_PATTERN =
/import\s*\{\s*defineFrontComponent\s*\}\s*from\s*['"][^'"]+['"];?\n?/g;
const DEFINE_FRONT_COMPONENT_EXPORT_PATTERN =
/export\s+default\s+defineFrontComponent\s*\(\s*\{[\s\S]*?component\s*:\s*(\w+)[\s\S]*?\}\s*\)\s*;?/s;
const DEFINE_FRONT_COMPONENT_EXPORT_OPENING_PATTERN =
/export\s+default\s+defineFrontComponent\s*\(/;
const IDENTIFIER_COMPONENT_VALUE_PATTERN =
/component\s*:\s*([A-Za-z_$][\w$]*)\s*[,}]/;
const FRONT_COMPONENT_DEFINITION_NAME = '__frontComponentDefinition';
export const unwrapDefineFrontComponentToDirectExport = (
sourceCode: string,
@@ -12,32 +17,32 @@ export const unwrapDefineFrontComponentToDirectExport = (
'',
);
const defineFrontComponentMatch = transformedSource.match(
DEFINE_FRONT_COMPONENT_EXPORT_PATTERN,
if (!DEFINE_FRONT_COMPONENT_EXPORT_OPENING_PATTERN.test(transformedSource)) {
return transformedSource;
}
transformedSource = transformedSource.replace(
DEFINE_FRONT_COMPONENT_EXPORT_OPENING_PATTERN,
`const ${FRONT_COMPONENT_DEFINITION_NAME} = (`,
);
if (defineFrontComponentMatch) {
const wrappedComponentName = defineFrontComponentMatch[1];
const identifierComponentMatch = transformedSource.match(
IDENTIFIER_COMPONENT_VALUE_PATTERN,
);
const exportedComponentDeclarationPattern = new RegExp(
`export\\s+(const|function)\\s+${wrappedComponentName}\\b`,
);
if (identifierComponentMatch) {
const wrappedComponentName = identifierComponentMatch[1];
transformedSource = transformedSource.replace(
exportedComponentDeclarationPattern,
new RegExp(`export\\s+(const|function)\\s+${wrappedComponentName}\\b`),
`$1 ${wrappedComponentName}`,
);
transformedSource =
`import { createRoot as __createRoot } from 'react-dom/client';\n` +
`import { jsx as __frontComponentJsx } from 'react/jsx-runtime';\n` +
transformedSource;
transformedSource = transformedSource.replace(
DEFINE_FRONT_COMPONENT_EXPORT_PATTERN,
`export default function __renderFrontComponent(__container) { __createRoot(__container).render(__frontComponentJsx(${wrappedComponentName}, {})); }`,
);
}
return transformedSource;
return (
`import { createRoot as __createRoot } from 'react-dom/client';\n` +
`import { jsx as __frontComponentJsx } from 'react/jsx-runtime';\n` +
transformedSource +
`\nexport default function __renderFrontComponent(__container) { __createRoot(__container).render(__frontComponentJsx(${FRONT_COMPONENT_DEFINITION_NAME}.component, {})); }\n`
);
};