[SDK] Extract twenty-front-component-renderer outside of twenty-sdk ( 2.8MB ) (#19021)

Followup https://github.com/twentyhq/twenty/pull/19010

## Dependency diagram

```
┌─────────────────────┐
│     twenty-front    │
│   (React frontend)  │
└─────────┬───────────┘
          │ imports runtime:
          │   FrontComponentRenderer
          │   FrontComponentRendererWithSdkClient
          │   useFrontComponentExecutionContext
          ▼
┌──────────────────────────────────┐         ┌─────────────────────────┐
│ twenty-front-component-renderer  │────────▶│       twenty-sdk        │
│   (remote-dom host + worker)     │         │  (app developer SDK)    │
│                                  │         │                         │
│  imports from twenty-sdk:        │         │  Public API:            │
│   • types only:                  │         │   defineFrontComponent  │
│     FrontComponentExecutionContext│         │   navigate, closeSide…  │
│     NavigateFunction             │         │   useFrontComponent…    │
│     CloseSidePanelFunction       │         │   Command components    │
│     CommandConfirmation…         │         │   conditional avail.    │
│     OpenCommandConfirmation…     │         │                         │
│     EnqueueSnackbarFunction      │         │  Internal only:         │
│     etc.                         │         │   frontComponentHost…   │
│                                  │         │   front-component-build │
│  owns locally:                   │         │   esbuild plugins       │
│   • ALLOWED_HTML_ELEMENTS        │         │                         │
│   • EVENT_TO_REACT               │         └────────────┬────────────┘
│   • HTML_TAG_TO_CUSTOM_ELEMENT…  │                      │
│   • SerializedEventData          │                      │ types
│   • PropertySchema               │                      ▼
│   • frontComponentHostComm…      │         ┌─────────────────────────┐
│     (local ref to globalThis)    │         │     twenty-shared       │
│   • setFrontComponentExecution…  │         │  (common types/utils)   │
│     (local impl, same keys)      │         │   AppPath, SidePanelP…  │
│                                  │         │   EnqueueSnackbarParams │
└──────────────────────────────────┘         │   isDefined, …          │
          │                                  └─────────────────────────┘
          │ also depends on
          ▼
    twenty-shared (types)
    @remote-dom/* (runtime)
    @quilted/threads (runtime)
    react (runtime)
```

**Key points:**

- **`twenty-front`** depends on the renderer, **not** on `twenty-sdk`
directly (for rendering)
- **`twenty-front-component-renderer`** depends on `twenty-sdk` for
**types only** (function signatures, `FrontComponentExecutionContext`).
The runtime bridge (`frontComponentHostCommunicationApi`) is shared via
`globalThis` keys, not module imports
- **`twenty-sdk`** has no dependency on the renderer — clean one-way
dependency
- The renderer owns all remote-dom infrastructure (element schemas,
event mappings, custom element tags) that was previously leaking through
the SDK's public API
- The SDK's `./build` entry point was removed entirely (unused)
This commit is contained in:
Paul Rastoin
2026-03-30 19:06:06 +02:00
committed by GitHub
parent 369ae2862f
commit 37908114fc
96 changed files with 921 additions and 431 deletions
@@ -0,0 +1,114 @@
name: CI Front Component Renderer
on:
pull_request:
merge_group:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
jobs:
changed-files-check:
if: github.event_name != 'merge_group'
uses: ./.github/workflows/changed-files.yaml
with:
files: |
package.json
yarn.lock
packages/twenty-front-component-renderer/**
packages/twenty-sdk/**
packages/twenty-shared/**
!packages/twenty-sdk/package.json
renderer-task:
needs: changed-files-check
if: needs.changed-files-check.outputs.any_changed == 'true'
timeout-minutes: 30
runs-on: ubuntu-latest
strategy:
matrix:
task: [build, typecheck, lint]
steps:
- name: Cancel Previous Runs
uses: styfle/cancel-workflow-action@0.11.0
with:
access_token: ${{ github.token }}
- name: Fetch custom Github Actions and base branch history
uses: actions/checkout@v4
with:
fetch-depth: 10
- name: Install dependencies
uses: ./.github/actions/yarn-install
- name: Run ${{ matrix.task }}
run: npx nx ${{ matrix.task }} twenty-front-component-renderer
renderer-sb-build:
needs: changed-files-check
if: needs.changed-files-check.outputs.any_changed == 'true'
timeout-minutes: 30
runs-on: ubuntu-latest
steps:
- name: Cancel Previous Runs
uses: styfle/cancel-workflow-action@0.11.0
with:
access_token: ${{ github.token }}
- name: Fetch custom Github Actions and base branch history
uses: actions/checkout@v4
with:
fetch-depth: 10
- name: Install dependencies
uses: ./.github/actions/yarn-install
- name: Build storybook
run: npx nx storybook:build twenty-front-component-renderer
- name: Upload storybook build
uses: actions/upload-artifact@v4
with:
name: storybook-twenty-front-component-renderer
path: packages/twenty-front-component-renderer/storybook-static
retention-days: 1
renderer-sb-test:
timeout-minutes: 30
runs-on: ubuntu-latest
needs: renderer-sb-build
env:
STORYBOOK_URL: http://localhost:6008
steps:
- name: Fetch custom Github Actions and base branch history
uses: actions/checkout@v4
with:
fetch-depth: 10
- name: Install dependencies
uses: ./.github/actions/yarn-install
- name: Build dependencies
run: npx nx build twenty-sdk
- name: Download storybook build
uses: actions/download-artifact@v4
with:
name: storybook-twenty-front-component-renderer
path: packages/twenty-front-component-renderer/storybook-static
- name: Install Playwright
run: |
cd packages/twenty-front-component-renderer
npx playwright install
- name: Serve storybook & run tests
run: |
npx http-server packages/twenty-front-component-renderer/storybook-static --port 6008 --silent &
timeout 30 bash -c 'until curl -sf http://localhost:6008 > /dev/null 2>&1; do sleep 1; done'
npx nx storybook:test twenty-front-component-renderer
ci-front-component-renderer-status-check:
if: always() && !cancelled()
timeout-minutes: 5
runs-on: ubuntu-latest
needs:
[
changed-files-check,
renderer-task,
renderer-sb-build,
renderer-sb-test,
]
steps:
- name: Fail job if any needs failed
if: contains(needs.*.result, 'failure')
run: exit 1
+2 -1
View File
@@ -25,6 +25,7 @@ jobs:
package.json
yarn.lock
packages/twenty-front/**
packages/twenty-front-component-renderer/**
packages/twenty-ui/**
packages/twenty-shared/**
packages/twenty-sdk/**
@@ -93,7 +94,7 @@ jobs:
run: |
npx nx build twenty-shared
npx nx build twenty-ui
npx nx build twenty-sdk
npx nx build twenty-front-component-renderer
- name: Download storybook build
uses: actions/download-artifact@v4
with:
+1 -4
View File
@@ -27,7 +27,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
task: [lint, typecheck, test:unit, storybook:build, storybook:test, test:integration]
task: [lint, typecheck, test:unit, test:integration]
steps:
- name: Cancel Previous Runs
uses: styfle/cancel-workflow-action@0.11.0
@@ -41,9 +41,6 @@ jobs:
uses: ./.github/actions/yarn-install
- name: Build
run: npx nx build twenty-sdk
- name: Install Playwright
if: contains(matrix.task, 'storybook')
run: npx playwright install chromium
- name: Run ${{ matrix.task }} task
uses: ./.github/actions/nx-affected
with:
+1
View File
@@ -208,6 +208,7 @@
"packages/twenty-e2e-testing",
"packages/twenty-shared",
"packages/twenty-sdk",
"packages/twenty-front-component-renderer",
"packages/twenty-client-sdk",
"packages/twenty-apps",
"packages/twenty-cli",
+2
View File
@@ -16,6 +16,7 @@ COPY ./packages/twenty-server/patches /app/packages/twenty-server/patches
COPY ./packages/twenty-ui/package.json /app/packages/twenty-ui/
COPY ./packages/twenty-shared/package.json /app/packages/twenty-shared/
COPY ./packages/twenty-front/package.json /app/packages/twenty-front/
COPY ./packages/twenty-front-component-renderer/package.json /app/packages/twenty-front-component-renderer/
COPY ./packages/twenty-sdk/package.json /app/packages/twenty-sdk/
COPY ./packages/twenty-client-sdk/package.json /app/packages/twenty-client-sdk/
@@ -51,6 +52,7 @@ FROM common-deps AS twenty-front-build
ARG REACT_APP_SERVER_BASE_URL
COPY ./packages/twenty-front /app/packages/twenty-front
COPY ./packages/twenty-front-component-renderer /app/packages/twenty-front-component-renderer
COPY ./packages/twenty-ui /app/packages/twenty-ui
COPY ./packages/twenty-shared /app/packages/twenty-shared
COPY ./packages/twenty-sdk /app/packages/twenty-sdk
@@ -0,0 +1,4 @@
node_modules
storybook-static
src/__stories__/example-sources-built
src/__stories__/example-sources-built-preact
@@ -0,0 +1,57 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": ["react", "typescript", "import", "unicorn"],
"categories": {
"correctness": "off"
},
"ignorePatterns": ["node_modules", "dist"],
"rules": {
"func-style": ["error", "declaration", { "allowArrowFunctions": true }],
"no-console": "off",
"no-control-regex": "off",
"no-debugger": "error",
"no-duplicate-imports": "error",
"no-undef": "off",
"no-unused-vars": "off",
"no-redeclare": "off",
"import/no-duplicates": "error",
"typescript/no-redeclare": "error",
"typescript/ban-ts-comment": "error",
"typescript/consistent-type-imports": [
"error",
{
"prefer": "type-imports",
"fixStyle": "inline-type-imports"
}
],
"typescript/explicit-function-return-type": "off",
"typescript/explicit-module-boundary-types": "off",
"typescript/no-empty-object-type": [
"error",
{
"allowInterfaces": "with-single-extends"
}
],
"typescript/no-empty-function": "off",
"typescript/no-explicit-any": "off",
"typescript/no-unused-vars": [
"warn",
{
"vars": "all",
"varsIgnorePattern": "^_",
"args": "after-used",
"argsIgnorePattern": "^_"
}
],
"react/no-unescaped-entities": "off",
"react/prop-types": "off",
"react/jsx-key": "off",
"react/display-name": "off",
"react/jsx-uses-react": "off",
"react/react-in-jsx-scope": "off",
"react/jsx-no-useless-fragment": "off",
"react/jsx-props-no-spreading": ["error", { "explicitSpread": "ignore" }],
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn"
}
}
@@ -8,10 +8,10 @@ const dirname =
? __dirname
: path.dirname(fileURLToPath(import.meta.url));
const sdkRoot = path.resolve(dirname, '..');
const packageRoot = path.resolve(dirname, '..');
const config: StorybookConfig = {
stories: ['../src/front-component-renderer/**/*.stories.@(js|jsx|ts|tsx)'],
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: ['@storybook/addon-vitest'],
@@ -23,11 +23,11 @@ const config: StorybookConfig = {
staticDirs: [
{
from: '../src/front-component-renderer/__stories__/example-sources-built',
from: '../src/__stories__/example-sources-built',
to: '/built',
},
{
from: '../src/front-component-renderer/__stories__/example-sources-built-preact',
from: '../src/__stories__/example-sources-built-preact',
to: '/built-preact',
},
],
@@ -57,7 +57,7 @@ const config: StorybookConfig = {
},
plugins: [
...(viteConfig.plugins ?? []),
tsconfigPaths({ root: sdkRoot }),
tsconfigPaths({ root: packageRoot }),
],
optimizeDeps: {
...viteConfig.optimizeDeps,
@@ -0,0 +1,58 @@
{
"name": "twenty-front-component-renderer",
"version": "0.8.0-canary.5",
"private": true,
"main": "dist/index.cjs",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"files": [
"dist",
"package.json"
],
"scripts": {
"build": "npx rimraf dist && npx vite build"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
},
"license": "AGPL-3.0",
"dependencies": {
"@quilted/threads": "^4.0.1",
"@remote-dom/core": "^1.10.1",
"@remote-dom/react": "^1.2.2",
"@sniptt/guards": "^0.2.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"zod": "^4.1.11"
},
"devDependencies": {
"@chakra-ui/react": "^3.33.0",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0",
"@mui/material": "^7.3.8",
"@storybook/addon-vitest": "^10.2.13",
"@storybook/react-vite": "^10.2.13",
"@types/node": "^24.0.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@vitest/browser-playwright": "^4.0.18",
"playwright": "^1.56.1",
"storybook": "^10.2.13",
"styled-components": "^6.1.0",
"ts-morph": "^25.0.0",
"tsx": "^4.7.0",
"twenty-sdk": "workspace:*",
"twenty-shared": "workspace:*",
"twenty-ui": "workspace:*",
"vite-plugin-dts": "^4.5.4",
"vite-tsconfig-paths": "^4.2.1"
},
"engines": {
"node": "^24.5.0",
"yarn": "^4.0.2"
}
}
@@ -0,0 +1,112 @@
{
"name": "twenty-front-component-renderer",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "packages/twenty-front-component-renderer/src",
"projectType": "library",
"tags": ["scope:frontend"],
"targets": {
"build": {
"executor": "nx:run-commands",
"cache": true,
"inputs": ["production", "^production"],
"dependsOn": ["^build"],
"outputs": ["{projectRoot}/dist"],
"options": {
"cwd": "{projectRoot}",
"commands": [
"npx rimraf dist && npx vite build -c vite.config.ts",
"tsgo -p tsconfig.lib.json --declaration --emitDeclarationOnly --noEmit false --outDir dist --rootDir src && npx tsc-alias -p tsconfig.lib.json --outDir dist"
],
"parallel": false
}
},
"typecheck": {},
"lint": {},
"generate-remote-dom-elements": {
"executor": "nx:run-commands",
"cache": true,
"dependsOn": ["^build"],
"inputs": [
"{projectRoot}/scripts/remote-dom/**/*",
"{projectRoot}/src/constants/**/*",
"{workspaceRoot}/packages/twenty-ui/src/**/index.ts",
"{workspaceRoot}/packages/twenty-ui/src/**/*.tsx"
],
"outputs": [
"{projectRoot}/src/host/generated/*",
"{projectRoot}/src/remote/generated/*"
],
"options": {
"cwd": "packages/twenty-front-component-renderer",
"command": "tsx -r tsconfig-paths/register scripts/remote-dom/generate-remote-dom-elements.ts"
},
"configurations": {
"verbose": {
"command": "tsx -r tsconfig-paths/register scripts/remote-dom/generate-remote-dom-elements.ts --verbose"
}
}
},
"storybook:prebuild": {
"executor": "nx:run-commands",
"cache": true,
"dependsOn": [
"generate-remote-dom-elements",
{
"target": "build:sdk",
"projects": "twenty-sdk"
},
{
"target": "build:individual",
"projects": "twenty-ui"
},
{
"target": "build:individual",
"projects": "twenty-shared"
}
],
"inputs": [
"{projectRoot}/scripts/front-component-stories/**/*",
"{projectRoot}/src/__stories__/example-sources/*",
"{workspaceRoot}/packages/twenty-sdk/src/cli/utilities/build/**/*"
],
"outputs": ["{projectRoot}/src/__stories__/example-sources-built/*"],
"options": {
"command": "tsx {projectRoot}/scripts/front-component-stories/build-source-examples.ts"
}
},
"storybook:build": {
"dependsOn": ["storybook:prebuild"],
"configurations": {
"test": {}
}
},
"storybook:serve:dev": {
"executor": "nx:run-commands",
"options": {
"port": 6008
}
},
"storybook:serve:static": {
"options": {
"buildTarget": "twenty-front-component-renderer:storybook:build",
"port": 6008
},
"configurations": {
"test": {}
}
},
"storybook:test": {
"dependsOn": ["storybook:prebuild"],
"options": {
"command": "vitest run --coverage --config vitest.storybook.config.ts --shard={args.shard}"
}
},
"storybook:test:no-coverage": {
"dependsOn": ["storybook:prebuild"],
"options": {
"command": "vitest run --config vitest.storybook.config.ts --shard={args.shard}"
}
},
"storybook:coverage": {}
}
}
@@ -3,20 +3,20 @@ import * as fs from 'node:fs';
import * as path from 'node:path';
import { fileURLToPath } from 'node:url';
import { getFrontComponentBuildPlugins } from '../../src/cli/utilities/build/common/front-component-build/utils/get-front-component-build-plugins';
import { getFrontComponentBuildPlugins } from 'twenty-sdk/front-component-renderer/build';
const dirname = path.dirname(fileURLToPath(import.meta.url));
const exampleSourcesDir = path.resolve(
dirname,
'../../src/front-component-renderer/__stories__/example-sources',
'../../src/__stories__/example-sources',
);
const exampleSourcesBuiltDir = path.resolve(
dirname,
'../../src/front-component-renderer/__stories__/example-sources-built',
'../../src/__stories__/example-sources-built',
);
const exampleSourcesBuiltPreactDir = path.resolve(
dirname,
'../../src/front-component-renderer/__stories__/example-sources-built-preact',
'../../src/__stories__/example-sources-built-preact',
);
const rootNodeModules = path.resolve(dirname, '../../../../node_modules');
@@ -26,7 +26,10 @@ const twentyUiIndividualIndex = path.resolve(
'../../../twenty-ui/dist/individual/individual-entry.js',
);
const sdkIndividualIndex = path.resolve(dirname, '../../dist/sdk/index.js');
const sdkIndividualIndex = path.resolve(
dirname,
'../../../twenty-sdk/dist/sdk/index.js',
);
const twentySharedIndividualDir = path.resolve(
dirname,
@@ -4,9 +4,9 @@ import * as path from 'path';
import { IndentationText, Project, QuoteKind } from 'ts-morph';
import { fileURLToPath } from 'url';
import { ALLOWED_HTML_ELEMENTS } from '../../src/sdk/front-component-api/constants/AllowedHtmlElements';
import { COMMON_HTML_EVENTS } from '../../src/sdk/front-component-api/constants/CommonHtmlEvents';
import { HTML_COMMON_PROPERTIES } from '../../src/sdk/front-component-api/constants/HtmlCommonProperties';
import { ALLOWED_HTML_ELEMENTS } from '../../src/constants/AllowedHtmlElements';
import { COMMON_HTML_EVENTS } from '../../src/constants/CommonHtmlEvents';
import { HTML_COMMON_PROPERTIES } from '../../src/constants/HtmlCommonProperties';
import {
type ComponentSchema,
@@ -19,15 +19,9 @@ import {
const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
const PACKAGE_PATH = path.resolve(SCRIPT_DIR, '../..');
const FRONT_COMPONENT_PATH = path.join(
PACKAGE_PATH,
'src/front-component-renderer',
);
const HOST_GENERATED_DIR = path.join(FRONT_COMPONENT_PATH, 'host/generated');
const REMOTE_GENERATED_DIR = path.join(
FRONT_COMPONENT_PATH,
'remote/generated',
);
const SRC_PATH = path.join(PACKAGE_PATH, 'src');
const HOST_GENERATED_DIR = path.join(SRC_PATH, 'host/generated');
const REMOTE_GENERATED_DIR = path.join(SRC_PATH, 'remote/generated');
const extractHtmlTag = (tag: string): string => tag.slice(5);
@@ -1,6 +1,6 @@
import type { Project, SourceFile } from 'ts-morph';
import { EVENT_TO_REACT } from '../../../src/sdk/front-component-api/constants/EventToReact';
import { EVENT_TO_REACT } from '../../../src/constants/EventToReact';
import { type ComponentSchema } from './schemas';
import { addExportedConst } from './utils';
@@ -336,8 +336,7 @@ export const generateRemoteElements = (
});
sourceFile.addImportDeclaration({
moduleSpecifier:
'../../../sdk/front-component-api/constants/SerializedEventData',
moduleSpecifier: '@/constants/SerializedEventData',
namedImports: [{ name: 'SerializedEventData', isTypeOnly: true }],
});
@@ -1,4 +1,4 @@
import { type PropertySchema } from '@/front-component-renderer/types/PropertySchema';
import { type PropertySchema } from './PropertySchema';
export const HTML_COMMON_PROPERTIES: Record<string, PropertySchema> = {
id: { type: 'string', optional: true },
@@ -0,0 +1,32 @@
import {
type CloseSidePanelFunction,
type EnqueueSnackbarFunction,
type NavigateFunction,
type OpenCommandConfirmationModalFunction,
type OpenSidePanelPageFunction,
type RequestAccessTokenRefreshFunction,
type UnmountFrontComponentFunction,
type UpdateProgressFunction,
} from 'twenty-sdk';
import { FRONT_COMPONENT_HOST_COMMUNICATION_API_KEY } from 'twenty-sdk/front-component-renderer';
type FrontComponentHostCommunicationApiStore = {
navigate?: NavigateFunction;
requestAccessTokenRefresh?: RequestAccessTokenRefreshFunction;
openSidePanelPage?: OpenSidePanelPageFunction;
openCommandConfirmationModal?: OpenCommandConfirmationModalFunction;
unmountFrontComponent?: UnmountFrontComponentFunction;
enqueueSnackbar?: EnqueueSnackbarFunction;
closeSidePanel?: CloseSidePanelFunction;
updateProgress?: UpdateProgressFunction;
};
(globalThis as Record<string, unknown>)[
FRONT_COMPONENT_HOST_COMMUNICATION_API_KEY
] ??= {};
export const frontComponentHostCommunicationApi: FrontComponentHostCommunicationApiStore =
(globalThis as Record<string, unknown>)[
FRONT_COMPONENT_HOST_COMMUNICATION_API_KEY
] as FrontComponentHostCommunicationApiStore;
@@ -1,10 +1,10 @@
import { FrontComponentErrorEffect } from '@/front-component-renderer/remote/components/FrontComponentErrorEffect';
import { FrontComponentHostCommunicationApiEffect } from '@/front-component-renderer/remote/components/FrontComponentHostCommunicationApiEffect';
import { FrontComponentUpdateContextEffect } from '@/front-component-renderer/remote/components/FrontComponentUpdateContextEffect';
import { type FrontComponentHostCommunicationApi } from '@/front-component-renderer/types/FrontComponentHostCommunicationApi';
import { type SdkClientUrls } from '@/front-component-renderer/types/HostToWorkerRenderContext';
import { type WorkerExports } from '@/front-component-renderer/types/WorkerExports';
import { type FrontComponentExecutionContext } from '@/sdk/front-component-api';
import { FrontComponentErrorEffect } from '@/remote/components/FrontComponentErrorEffect';
import { FrontComponentHostCommunicationApiEffect } from '@/remote/components/FrontComponentHostCommunicationApiEffect';
import { FrontComponentUpdateContextEffect } from '@/remote/components/FrontComponentUpdateContextEffect';
import { type FrontComponentHostCommunicationApi } from '@/types/FrontComponentHostCommunicationApi';
import { type SdkClientUrls } from '@/types/HostToWorkerRenderContext';
import { type WorkerExports } from '@/types/WorkerExports';
import { type FrontComponentExecutionContext } from 'twenty-sdk';
import { type ThreadWebWorker } from '@quilted/threads';
import {
type RemoteReceiver,
@@ -1,7 +1,7 @@
import React from 'react';
import { EVENT_TO_REACT } from '@/sdk/front-component-api/constants/EventToReact';
import { type SerializedEventData } from '@/sdk/front-component-api/constants/SerializedEventData';
import { EVENT_TO_REACT } from '@/constants/EventToReact';
import { type SerializedEventData } from '@/constants/SerializedEventData';
const INTERNAL_PROPS = new Set(['element', 'receiver', 'components']);
@@ -0,0 +1,136 @@
export { FrontComponentRenderer } from './host/components/FrontComponentRenderer';
export { componentRegistry } from './host/generated/host-component-registry';
export { FrontComponentErrorEffect } from './remote/components/FrontComponentErrorEffect';
export { FrontComponentHostCommunicationApiEffect } from './remote/components/FrontComponentHostCommunicationApiEffect';
export { FrontComponentUpdateContextEffect } from './remote/components/FrontComponentUpdateContextEffect';
export { FrontComponentWorkerEffect } from './remote/components/FrontComponentWorkerEffect';
export {
HtmlA,
HtmlArticle,
HtmlAside,
HtmlBlockquote,
HtmlBr,
HtmlButton,
HtmlCode,
HtmlDiv,
HtmlEm,
HtmlFooter,
HtmlForm,
HtmlH1,
HtmlH2,
HtmlH3,
HtmlH4,
HtmlH5,
HtmlH6,
HtmlHeader,
HtmlHr,
HtmlIframe,
HtmlAudio,
HtmlImg,
HtmlSource,
HtmlVideo,
HtmlInput,
HtmlLabel,
HtmlLi,
HtmlMain,
HtmlNav,
HtmlOl,
HtmlOption,
HtmlP,
HtmlPre,
HtmlSection,
HtmlSelect,
HtmlSmall,
HtmlSpan,
HtmlStrong,
HtmlTable,
HtmlTbody,
HtmlTd,
HtmlTextarea,
HtmlTfoot,
HtmlTh,
HtmlThead,
HtmlTr,
HtmlUl,
} from './remote/generated/remote-components';
export {
HtmlAElement,
HtmlArticleElement,
HtmlAsideElement,
HtmlBlockquoteElement,
HtmlBrElement,
HtmlButtonElement,
HtmlCodeElement,
HtmlDivElement,
HtmlEmElement,
HtmlFooterElement,
HtmlFormElement,
HtmlH1Element,
HtmlH2Element,
HtmlH3Element,
HtmlH4Element,
HtmlH5Element,
HtmlH6Element,
HtmlHeaderElement,
HtmlHrElement,
HtmlIframeElement,
HtmlAudioElement,
HtmlImgElement,
HtmlSourceElement,
HtmlVideoElement,
HtmlInputElement,
HtmlLabelElement,
HtmlLiElement,
HtmlMainElement,
HtmlNavElement,
HtmlOlElement,
HtmlOptionElement,
HtmlPElement,
HtmlPreElement,
HtmlSectionElement,
HtmlSelectElement,
HtmlSmallElement,
HtmlSpanElement,
HtmlStrongElement,
HtmlTableElement,
HtmlTbodyElement,
HtmlTdElement,
HtmlTextareaElement,
HtmlTfootElement,
HtmlTheadElement,
HtmlThElement,
HtmlTrElement,
HtmlUlElement,
RemoteFragmentElement,
RemoteRootElement,
} from './remote/generated/remote-elements';
export type {
HtmlAProperties,
HtmlButtonProperties,
HtmlCommonEvents,
HtmlCommonProperties,
HtmlFormProperties,
HtmlIframeProperties,
HtmlAudioProperties,
HtmlImgProperties,
HtmlSourceProperties,
HtmlVideoProperties,
HtmlInputProperties,
HtmlLabelProperties,
HtmlOptionProperties,
HtmlSelectProperties,
HtmlTdProperties,
HtmlTextareaProperties,
HtmlThProperties,
} from './remote/generated/remote-elements';
export { createRemoteWorker } from './remote/worker/utils/createRemoteWorker';
export { installStyleBridge } from './polyfills/installStyleBridge';
export { exposeGlobals } from './remote/utils/exposeGlobals';
export type { FrontComponentExecutionContext } from 'twenty-sdk';
export type { FrontComponentHostCommunicationApi } from './types/FrontComponentHostCommunicationApi';
export type {
HostToWorkerRenderContext,
SdkClientUrls,
} from './types/HostToWorkerRenderContext';
export type { PropertySchema } from './constants/PropertySchema';
export type { WorkerExports } from './types/WorkerExports';
@@ -1,6 +1,6 @@
import { type RemoteRootElement } from '@remote-dom/core/elements';
import { type RemoteStyleProperties } from '@/front-component-renderer/remote/generated/remote-elements';
import { type RemoteStyleProperties } from '@/remote/generated/remote-elements';
import { MockCSSStyleSheet } from './MockCSSStyleSheet';
export const installStyleBridge = (remoteRoot: RemoteRootElement): void => {
@@ -1,5 +1,5 @@
import { type FrontComponentHostCommunicationApi } from '@/front-component-renderer/types/FrontComponentHostCommunicationApi';
import { type WorkerExports } from '@/front-component-renderer/types/WorkerExports';
import { type FrontComponentHostCommunicationApi } from '@/types/FrontComponentHostCommunicationApi';
import { type WorkerExports } from '@/types/WorkerExports';
import { type ThreadWebWorker } from '@quilted/threads';
import { useEffect } from 'react';
@@ -1,6 +1,6 @@
import { type FrontComponentHostCommunicationApi } from '@/front-component-renderer/types/FrontComponentHostCommunicationApi';
import { type WorkerExports } from '@/front-component-renderer/types/WorkerExports';
import { type FrontComponentExecutionContext } from '@/sdk/front-component-api';
import { type FrontComponentHostCommunicationApi } from '@/types/FrontComponentHostCommunicationApi';
import { type WorkerExports } from '@/types/WorkerExports';
import { type FrontComponentExecutionContext } from 'twenty-sdk';
import { type ThreadWebWorker } from '@quilted/threads';
import { useEffect } from 'react';
@@ -2,7 +2,7 @@ import { ThreadWebWorker, release, retain } from '@quilted/threads';
import { RemoteReceiver } from '@remote-dom/core/receivers';
import { useEffect, useRef } from 'react';
import { type ConfirmationModalCaller } from 'twenty-shared/types';
import { type CommandConfirmationModalResult } from '../../../sdk/front-component-api/globals/frontComponentHostCommunicationApi';
import { type CommandConfirmationModalResult } from 'twenty-sdk';
import { type FrontComponentHostCommunicationApi } from '../../types/FrontComponentHostCommunicationApi';
import { type SdkClientUrls } from '../../types/HostToWorkerRenderContext';
import { type WorkerExports } from '../../types/WorkerExports';
@@ -4,7 +4,7 @@ import {
RemoteFragmentElement,
type RemoteEvent,
} from '@remote-dom/core/elements';
import { type SerializedEventData } from '../../../sdk/front-component-api/constants/SerializedEventData';
import { type SerializedEventData } from '@/constants/SerializedEventData';
export type HtmlCommonProperties = {
id?: string;
@@ -1,4 +1,4 @@
import { ALLOWED_HTML_ELEMENTS } from '@/sdk/front-component-api/constants/AllowedHtmlElements';
import { ALLOWED_HTML_ELEMENTS } from '@/constants/AllowedHtmlElements';
const camelToKebab = (property: string): string =>
property.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
@@ -1,4 +1,4 @@
import { ALLOWED_HTML_ELEMENTS } from '@/sdk/front-component-api/constants/AllowedHtmlElements';
import { ALLOWED_HTML_ELEMENTS } from '@/constants/AllowedHtmlElements';
const ATTRIBUTE_TO_PROPERTY_MAP: Record<string, string> = {
className: 'className',
@@ -12,14 +12,13 @@ import {
import { isDefined } from 'twenty-shared/utils';
import { installStyleBridge } from '@/front-component-renderer/polyfills/installStyleBridge';
import { installStylePropertyOnRemoteElements } from '@/front-component-renderer/remote/utils/installStylePropertyOnRemoteElements';
import { patchRemoteElementSetAttribute } from '@/front-component-renderer/remote/utils/patchRemoteElementSetAttribute';
import { HTML_TAG_TO_CUSTOM_ELEMENT_TAG } from '@/sdk/front-component-api/constants/HtmlTagToRemoteComponent';
import { setFrontComponentExecutionContext } from '@/sdk/front-component-api/context/frontComponentContext';
import { frontComponentHostCommunicationApi } from '@/sdk/front-component-api/globals/frontComponentHostCommunicationApi';
import { type FrontComponentExecutionContext } from '@/sdk/front-component-api';
import { installStyleBridge } from '@/polyfills/installStyleBridge';
import { installStylePropertyOnRemoteElements } from '@/remote/utils/installStylePropertyOnRemoteElements';
import { patchRemoteElementSetAttribute } from '@/remote/utils/patchRemoteElementSetAttribute';
import { type FrontComponentExecutionContext } from 'twenty-sdk';
import { frontComponentHostCommunicationApi } from '@/constants/frontComponentHostCommunicationApi';
import { HTML_TAG_TO_CUSTOM_ELEMENT_TAG } from '@/constants/HtmlTagToRemoteComponent';
import { setFrontComponentExecutionContext } from './utils/setFrontComponentExecutionContext';
import { type FrontComponentHostCommunicationApi } from '../../types/FrontComponentHostCommunicationApi';
import { type HostToWorkerRenderContext } from '../../types/HostToWorkerRenderContext';
import { type WorkerExports } from '../../types/WorkerExports';
@@ -1,7 +1,7 @@
import {
type CommandConfirmationModalResult,
type OpenCommandConfirmationModalFunction,
} from '@/sdk/front-component-api/globals/frontComponentHostCommunicationApi';
} from 'twenty-sdk';
import { type FrontComponentHostCommunicationApi } from '../../../types/FrontComponentHostCommunicationApi';
type CommandConfirmationModalPromiseCallbacks = {
@@ -0,0 +1,30 @@
import { type FrontComponentExecutionContext } from 'twenty-sdk';
import {
FRONT_COMPONENT_CONTEXT_KEY,
FRONT_COMPONENT_LISTENERS_KEY,
} from 'twenty-sdk/front-component-renderer';
type Listener = () => void;
const getListeners = (): Set<Listener> => {
if (!(globalThis as Record<string, unknown>)[FRONT_COMPONENT_LISTENERS_KEY]) {
(globalThis as Record<string, unknown>)[FRONT_COMPONENT_LISTENERS_KEY] =
new Set<Listener>();
}
return (globalThis as Record<string, unknown>)[
FRONT_COMPONENT_LISTENERS_KEY
] as Set<Listener>;
};
export const setFrontComponentExecutionContext = (
context: FrontComponentExecutionContext,
): void => {
(globalThis as Record<string, unknown>)[FRONT_COMPONENT_CONTEXT_KEY] =
context;
for (const listener of getListeners()) {
listener();
}
};
@@ -7,7 +7,7 @@ import {
type RequestAccessTokenRefreshFunction,
type UnmountFrontComponentFunction,
type UpdateProgressFunction,
} from '../../sdk/front-component-api/globals/frontComponentHostCommunicationApi';
} from 'twenty-sdk';
export type FrontComponentHostCommunicationApi = {
navigate: NavigateFunction;
@@ -0,0 +1,37 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"allowJs": false,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"jsx": "react-jsx",
"moduleResolution": "bundler",
"strictNullChecks": true,
"alwaysStrict": true,
"noImplicitAny": true,
"strictBindCallApply": false,
"noEmit": true,
"types": ["jest", "node"],
"paths": {
"@/*": ["./src/*"]
}
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.d.ts",
"scripts/**/*.ts",
".storybook/*.ts",
".storybook/*.tsx",
"**/__mocks__/**/*",
"**/__tests__/**/*",
"vite.config.ts"
],
"exclude": [
"src/remote/mock/**/*",
"src/host/generated/host-component-registry.ts",
"src/remote/generated/remote-components.ts",
"src/remote/generated/remote-elements.ts",
"src/__stories__/**/*"
]
}
@@ -0,0 +1,27 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"resolveJsonModule": true,
"declaration": false,
"sourceMap": true
},
"include": ["src"],
"exclude": [
"node_modules",
"dist",
"**/*.test.ts",
"**/*.spec.ts",
"**/*.e2e-spec.ts",
"**/__tests__/**",
"**/__stories__/**",
"**/*.stories.ts",
"**/*.stories.tsx"
]
}
@@ -0,0 +1,81 @@
import path from 'path';
import { type PackageJson } from 'type-fest';
import { defineConfig } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths';
import packageJson from './package.json';
export default defineConfig(() => {
return {
root: __dirname,
cacheDir:
'../../node_modules/.vite/packages/twenty-front-component-renderer',
resolve: {
alias: {
'@/': path.resolve(__dirname, 'src') + '/',
},
},
plugins: [
tsconfigPaths({
root: __dirname,
}),
],
worker: {
format: 'iife',
rollupOptions: {
output: {
inlineDynamicImports: true,
},
},
plugins: () => [
{
name: 'define-process-env',
transform: (code: string) =>
code
.replace(/process\.env\.NODE_ENV/g, JSON.stringify('production'))
.replace(/process\.env/g, '{}'),
},
],
},
build: {
emptyOutDir: false,
outDir: 'dist',
lib: {
entry: 'src/index.ts',
name: 'twenty-front-component-renderer',
},
rollupOptions: {
onwarn: (warning, warn) => {
if (
warning.code === 'MODULE_LEVEL_DIRECTIVE' &&
warning.message.includes('"use client"')
) {
return;
}
warn(warning);
},
external: (id: string) => {
const deps = Object.keys(
(packageJson as PackageJson).dependencies || {},
);
return deps.some((dep) => id === dep || id.startsWith(dep + '/'));
},
output: [
{
format: 'es',
entryFileNames: '[name].mjs',
},
{
format: 'cjs',
interop: 'auto',
esModule: true,
exports: 'named',
entryFileNames: '[name].cjs',
},
],
},
},
logLevel: 'warn',
};
});
@@ -20,7 +20,7 @@ export default defineConfig({
},
projects: [
{
extends: './vite.config.browser.ts',
extends: './vite.config.ts',
plugins: [
storybookTest({
configDir: path.join(dirname, '.storybook'),
+1 -1
View File
@@ -115,7 +115,7 @@
"react-textarea-autosize": "^8.4.1",
"remark-gfm": "^4.0.1",
"transliteration": "^2.3.5",
"twenty-sdk": "workspace:*",
"twenty-front-component-renderer": "workspace:*",
"twenty-shared": "workspace:*",
"twenty-ui": "workspace:*",
"use-debounce": "^10.0.0"
@@ -8,7 +8,7 @@ import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
import { t } from '@lingui/core/macro';
import { useCallback, useContext, useEffect } from 'react';
import { FrontComponentRenderer as SharedFrontComponentRenderer } from 'twenty-sdk/front-component-renderer';
import { FrontComponentRenderer as SharedFrontComponentRenderer } from 'twenty-front-component-renderer';
import { isDefined } from 'twenty-shared/utils';
import { ThemeContext } from 'twenty-ui/theme-constants';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
@@ -6,7 +6,7 @@ import {
FrontComponentRenderer as SharedFrontComponentRenderer,
type FrontComponentExecutionContext,
type FrontComponentHostCommunicationApi,
} from 'twenty-sdk/front-component-renderer';
} from 'twenty-front-component-renderer';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
type FrontComponentRendererWithSdkClientProps = {
@@ -2,7 +2,7 @@ import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomStat
import {
type FrontComponentExecutionContext,
type FrontComponentHostCommunicationApi,
} from 'twenty-sdk/front-component-renderer';
} from 'twenty-front-component-renderer';
import { type AppPath, type EnqueueSnackbarParams } from 'twenty-shared/types';
import { currentUserState } from '@/auth/states/currentUserState';
+9 -18
View File
@@ -41,24 +41,19 @@
},
"./front-component-renderer": {
"types": "./dist/front-component-renderer/index.d.ts",
"import": "./dist/front-component-renderer/index.mjs",
"require": "./dist/front-component-renderer/index.cjs"
"import": "./dist/front-component-renderer.mjs",
"require": "./dist/front-component-renderer.cjs"
},
"./build": {
"types": "./dist/build/index.d.ts",
"import": "./dist/build.mjs",
"require": "./dist/build.cjs"
"./front-component-renderer/build": {
"types": "./dist/front-component-renderer/build/index.d.ts",
"import": "./dist/front-component-renderer/build.mjs",
"require": "./dist/front-component-renderer/build.cjs"
}
},
"license": "AGPL-3.0",
"dependencies": {
"@chakra-ui/react": "^3.33.0",
"@emotion/react": "^11.14.0",
"@genql/cli": "^3.0.3",
"@genql/runtime": "^2.10.0",
"@quilted/threads": "^4.0.1",
"@remote-dom/core": "^1.10.1",
"@remote-dom/react": "^1.2.2",
"@sniptt/guards": "^0.2.0",
"axios": "^1.13.5",
"chalk": "^5.3.0",
@@ -83,18 +78,11 @@
"zod": "^4.1.11"
},
"devDependencies": {
"@emotion/styled": "^11.14.0",
"@mui/material": "^7.3.8",
"@prettier/sync": "^0.5.2",
"@storybook/addon-vitest": "^10.2.13",
"@storybook/react-vite": "^10.2.13",
"@types/inquirer": "^9.0.0",
"@types/node": "^24.0.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@vitest/browser-playwright": "^4.0.18",
"playwright": "^1.56.1",
"storybook": "^10.2.13",
"ts-morph": "^25.0.0",
"tsx": "^4.7.0",
"twenty-shared": "workspace:*",
@@ -116,6 +104,9 @@
],
"front-component-renderer": [
"dist/front-component-renderer/index.d.ts"
],
"front-component-renderer/build": [
"dist/front-component-renderer/build/index.d.ts"
]
}
}
+1 -86
View File
@@ -95,91 +95,6 @@
"cwd": "{projectRoot}",
"command": "npx vite build -c vite.config.sdk.ts"
}
},
"generate-remote-dom-elements": {
"executor": "nx:run-commands",
"cache": true,
"dependsOn": ["^build"],
"inputs": [
"{projectRoot}/scripts/remote-dom/**/*",
"{projectRoot}/src/sdk/front-component-api/**/*",
"{workspaceRoot}/packages/twenty-ui/src/**/index.ts",
"{workspaceRoot}/packages/twenty-ui/src/**/*.tsx"
],
"outputs": [
"{projectRoot}/src/front-component-renderer/host/generated/*",
"{projectRoot}/src/front-component-renderer/remote/generated/*"
],
"options": {
"cwd": "packages/twenty-sdk",
"command": "tsx -r tsconfig-paths/register scripts/remote-dom/generate-remote-dom-elements.ts"
},
"configurations": {
"verbose": {
"command": "tsx -r tsconfig-paths/register scripts/remote-dom/generate-remote-dom-elements.ts --verbose"
}
}
},
"storybook:prebuild": {
"executor": "nx:run-commands",
"cache": true,
"dependsOn": [
"generate-remote-dom-elements",
"build:sdk",
{
"target": "build:individual",
"projects": "twenty-ui"
},
{
"target": "build:individual",
"projects": "twenty-shared"
}
],
"inputs": [
"{projectRoot}/scripts/front-component-stories/**/*",
"{projectRoot}/src/front-component-renderer/__stories__/example-sources/*",
"{projectRoot}/src/cli/utilities/build/**/*"
],
"outputs": [
"{projectRoot}/src/front-component-renderer/__stories__/example-sources-built/*"
],
"options": {
"command": "tsx {projectRoot}/scripts/front-component-stories/build-source-examples.ts"
}
},
"storybook:build": {
"dependsOn": ["storybook:prebuild"],
"configurations": {
"test": {}
}
},
"storybook:serve:dev": {
"executor": "nx:run-commands",
"options": {
"port": 6008
}
},
"storybook:serve:static": {
"options": {
"buildTarget": "twenty-sdk:storybook:build",
"port": 6008
},
"configurations": {
"test": {}
}
},
"storybook:test": {
"dependsOn": ["storybook:prebuild"],
"options": {
"command": "vitest run --coverage --config vitest.storybook.config.ts --shard={args.shard}"
}
},
"storybook:test:no-coverage": {
"dependsOn": ["storybook:prebuild"],
"options": {
"command": "vitest run --config vitest.storybook.config.ts --shard={args.shard}"
}
},
"storybook:coverage": {}
}
}
}
-5
View File
@@ -1,5 +0,0 @@
export { getFrontComponentBuildPlugins } from '@/cli/utilities/build/common/front-component-build/utils/get-front-component-build-plugins';
export { getBaseFrontComponentBuildOptions } from '@/cli/utilities/build/common/front-component-build/utils/get-base-front-component-build-options';
export { FRONT_COMPONENT_EXTERNAL_MODULES } from '@/cli/utilities/build/common/front-component-build/constants/front-component-external-modules';
export { processEsbuildResult } from '@/cli/utilities/build/common/esbuild-result-processor';
export type { ProcessEsbuildResultParams } from '@/cli/utilities/build/common/esbuild-result-processor';
@@ -0,0 +1 @@
export { getFrontComponentBuildPlugins } from '@/cli/utilities/build/common/front-component-build/utils/get-front-component-build-plugins';
@@ -1,134 +1,3 @@
export { FrontComponentRenderer } from './host/components/FrontComponentRenderer';
export { componentRegistry } from './host/generated/host-component-registry';
export { FrontComponentErrorEffect } from './remote/components/FrontComponentErrorEffect';
export { FrontComponentHostCommunicationApiEffect } from './remote/components/FrontComponentHostCommunicationApiEffect';
export { FrontComponentUpdateContextEffect } from './remote/components/FrontComponentUpdateContextEffect';
export { FrontComponentWorkerEffect } from './remote/components/FrontComponentWorkerEffect';
export {
HtmlA,
HtmlArticle,
HtmlAside,
HtmlBlockquote,
HtmlBr,
HtmlButton,
HtmlCode,
HtmlDiv,
HtmlEm,
HtmlFooter,
HtmlForm,
HtmlH1,
HtmlH2,
HtmlH3,
HtmlH4,
HtmlH5,
HtmlH6,
HtmlHeader,
HtmlHr,
HtmlIframe,
HtmlAudio,
HtmlImg,
HtmlSource,
HtmlVideo,
HtmlInput,
HtmlLabel,
HtmlLi,
HtmlMain,
HtmlNav,
HtmlOl,
HtmlOption,
HtmlP,
HtmlPre,
HtmlSection,
HtmlSelect,
HtmlSmall,
HtmlSpan,
HtmlStrong,
HtmlTable,
HtmlTbody,
HtmlTd,
HtmlTextarea,
HtmlTfoot,
HtmlTh,
HtmlThead,
HtmlTr,
HtmlUl,
} from './remote/generated/remote-components';
export {
HtmlAElement,
HtmlArticleElement,
HtmlAsideElement,
HtmlBlockquoteElement,
HtmlBrElement,
HtmlButtonElement,
HtmlCodeElement,
HtmlDivElement,
HtmlEmElement,
HtmlFooterElement,
HtmlFormElement,
HtmlH1Element,
HtmlH2Element,
HtmlH3Element,
HtmlH4Element,
HtmlH5Element,
HtmlH6Element,
HtmlHeaderElement,
HtmlHrElement,
HtmlIframeElement,
HtmlAudioElement,
HtmlImgElement,
HtmlSourceElement,
HtmlVideoElement,
HtmlInputElement,
HtmlLabelElement,
HtmlLiElement,
HtmlMainElement,
HtmlNavElement,
HtmlOlElement,
HtmlOptionElement,
HtmlPElement,
HtmlPreElement,
HtmlSectionElement,
HtmlSelectElement,
HtmlSmallElement,
HtmlSpanElement,
HtmlStrongElement,
HtmlTableElement,
HtmlTbodyElement,
HtmlTdElement,
HtmlTextareaElement,
HtmlTfootElement,
HtmlTheadElement,
HtmlThElement,
HtmlTrElement,
HtmlUlElement,
RemoteFragmentElement,
RemoteRootElement,
} from './remote/generated/remote-elements';
export type {
HtmlAProperties,
HtmlButtonProperties,
HtmlCommonEvents,
HtmlCommonProperties,
HtmlFormProperties,
HtmlIframeProperties,
HtmlAudioProperties,
HtmlImgProperties,
HtmlSourceProperties,
HtmlVideoProperties,
HtmlInputProperties,
HtmlLabelProperties,
HtmlOptionProperties,
HtmlSelectProperties,
HtmlTdProperties,
HtmlTextareaProperties,
HtmlThProperties,
} from './remote/generated/remote-elements';
export { createRemoteWorker } from './remote/worker/utils/createRemoteWorker';
export type { FrontComponentExecutionContext } from '../sdk/front-component-api';
export type { FrontComponentHostCommunicationApi } from './types/FrontComponentHostCommunicationApi';
export type {
HostToWorkerRenderContext,
SdkClientUrls,
} from './types/HostToWorkerRenderContext';
export type { PropertySchema } from './types/PropertySchema';
export type { WorkerExports } from './types/WorkerExports';
export { FRONT_COMPONENT_CONTEXT_KEY } from '@/sdk/front-component-api/constants/front-component-context-key';
export { FRONT_COMPONENT_HOST_COMMUNICATION_API_KEY } from '@/sdk/front-component-api/constants/front-component-host-communication-api-key';
export { FRONT_COMPONENT_LISTENERS_KEY } from '@/sdk/front-component-api/constants/front-component-listeners-key';
@@ -0,0 +1 @@
export const FRONT_COMPONENT_CONTEXT_KEY = '__twentySdkExecutionContext__';
@@ -0,0 +1,2 @@
export const FRONT_COMPONENT_HOST_COMMUNICATION_API_KEY =
'frontComponentHostCommunicationApi';
@@ -0,0 +1 @@
export const FRONT_COMPONENT_LISTENERS_KEY = '__twentySdkContextListeners__';
@@ -1,35 +1,25 @@
import { FRONT_COMPONENT_CONTEXT_KEY } from '../constants/front-component-context-key';
import { FRONT_COMPONENT_LISTENERS_KEY } from '../constants/front-component-listeners-key';
import { type FrontComponentExecutionContext } from '../types/FrontComponentExecutionContext';
type Listener = () => void;
const CONTEXT_KEY = '__twentySdkExecutionContext__';
const LISTENERS_KEY = '__twentySdkContextListeners__';
const getListeners = (): Set<Listener> => {
if (!(globalThis as Record<string, unknown>)[LISTENERS_KEY]) {
(globalThis as Record<string, unknown>)[LISTENERS_KEY] =
if (!(globalThis as Record<string, unknown>)[FRONT_COMPONENT_LISTENERS_KEY]) {
(globalThis as Record<string, unknown>)[FRONT_COMPONENT_LISTENERS_KEY] =
new Set<Listener>();
}
return (globalThis as Record<string, unknown>)[
LISTENERS_KEY
FRONT_COMPONENT_LISTENERS_KEY
] as Set<Listener>;
};
export const setFrontComponentExecutionContext = (
context: FrontComponentExecutionContext,
): void => {
(globalThis as Record<string, unknown>)[CONTEXT_KEY] = context;
for (const listener of getListeners()) {
listener();
}
};
export const getFrontComponentExecutionContext =
(): FrontComponentExecutionContext => {
return (globalThis as Record<string, unknown>)[
CONTEXT_KEY
FRONT_COMPONENT_CONTEXT_KEY
] as FrontComponentExecutionContext;
};
@@ -58,11 +58,13 @@ export type FrontComponentHostCommunicationApiStore = {
updateProgress?: UpdateProgressFunction;
};
import { FRONT_COMPONENT_HOST_COMMUNICATION_API_KEY } from '../constants/front-component-host-communication-api-key';
declare global {
var frontComponentHostCommunicationApi: FrontComponentHostCommunicationApiStore;
}
globalThis.frontComponentHostCommunicationApi ??= {};
globalThis[FRONT_COMPONENT_HOST_COMMUNICATION_API_KEY] ??= {};
export const frontComponentHostCommunicationApi =
globalThis.frontComponentHostCommunicationApi;
@@ -26,7 +26,6 @@ export {
includesEvery,
objectMetadataItem,
} from './conditional-availability/conditional-availability-variables';
export { setFrontComponentExecutionContext } from './context/frontComponentContext';
export { closeSidePanel } from './functions/closeSidePanel';
export { enqueueSnackbar } from './functions/enqueueSnackbar';
export { navigate } from './functions/navigate';
@@ -41,14 +40,15 @@ export { useUserId } from './hooks/useUserId';
export type { FrontComponentExecutionContext } from './types/FrontComponentExecutionContext';
export { getFrontComponentCommandErrorDedupeKey } from './utils/getFrontComponentCommandErrorDedupeKey';
export type {
CloseSidePanelFunction,
CommandConfirmationModalAccent,
CommandConfirmationModalResult,
EnqueueSnackbarFunction,
NavigateFunction,
OpenCommandConfirmationModalFunction,
OpenCommandConfirmationModalHostFunction,
OpenSidePanelPageFunction,
RequestAccessTokenRefreshFunction,
UnmountFrontComponentFunction,
UpdateProgressFunction,
} from './globals/frontComponentHostCommunicationApi';
export { ALLOWED_HTML_ELEMENTS } from './constants/AllowedHtmlElements';
export type { AllowedHtmlElement } from './constants/AllowedHtmlElements';
export { COMMON_HTML_EVENTS } from './constants/CommonHtmlEvents';
export { EVENT_TO_REACT } from './constants/EventToReact';
export { HTML_COMMON_PROPERTIES } from './constants/HtmlCommonProperties';
export { HTML_TAG_TO_REMOTE_COMPONENT } from './constants/HtmlTagToRemoteComponent';
export type { SerializedEventData } from './constants/SerializedEventData';
+9 -14
View File
@@ -135,9 +135,18 @@ export {
useUserId,
} from './front-component-api';
export type {
CloseSidePanelFunction,
CommandConfirmationModalAccent,
CommandConfirmationModalResult,
EnqueueSnackbarFunction,
FrontComponentExecutionContext,
NavigateFunction,
OpenCommandConfirmationModalFunction,
OpenCommandConfirmationModalHostFunction,
OpenSidePanelPageFunction,
RequestAccessTokenRefreshFunction,
UnmountFrontComponentFunction,
UpdateProgressFunction,
} from './front-component-api';
export { AppPath, SidePanelPages } from 'twenty-shared/types';
@@ -145,17 +154,3 @@ export type {
EnqueueSnackbarParams,
SnackBarVariant,
} from 'twenty-shared/types';
// Front Component Common exports
export {
ALLOWED_HTML_ELEMENTS,
COMMON_HTML_EVENTS,
EVENT_TO_REACT,
HTML_COMMON_PROPERTIES,
HTML_TAG_TO_REMOTE_COMPONENT,
} from './front-component-api';
export type { AllowedHtmlElement } from './front-component-api';
// Style bridge utilities for CSS-in-JS libraries in remote components
export { installStyleBridge } from '../front-component-renderer/polyfills/installStyleBridge';
export { exposeGlobals } from '../front-component-renderer/remote/utils/exposeGlobals';
-10
View File
@@ -20,9 +20,6 @@
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.d.ts",
"scripts/**/*.ts",
".storybook/*.ts",
".storybook/*.tsx",
"**/__mocks__/**/*",
"**/__tests__/**/*",
"vite.config.ts",
@@ -30,12 +27,5 @@
"vite.config.browser.ts",
"vite.config.sdk.ts",
"jest.config.mjs"
],
"exclude": [
"src/front-component-renderer/remote/mock/**/*",
"src/front-component-renderer/host/generated/host-component-registry.ts",
"src/front-component-renderer/remote/generated/remote-components.ts",
"src/front-component-renderer/remote/generated/remote-elements.ts",
"src/front-component-renderer/__stories__/**/*"
]
}
+36 -42
View File
@@ -5,28 +5,6 @@ import tsconfigPaths from 'vite-tsconfig-paths';
import packageJson from './package.json';
const entries = ['src/ui/index.ts', 'src/front-component-renderer/index.ts'];
const entryFileNames = (chunk: any, extension: 'cjs' | 'mjs') => {
if (!chunk.isEntry) {
throw new Error(
`Should never occur, encountered a non-entry chunk ${chunk.facadeModuleId}`,
);
}
const entry = entries.find((entryPath) =>
chunk.facadeModuleId?.endsWith(entryPath),
);
if (!entry) {
return `${chunk.name}.${extension}`;
}
const modulePath = entry.replace('src/', '').replace('/index.ts', '');
return `${modulePath}/index.${extension}`;
};
export default defineConfig(() => {
return {
root: __dirname,
@@ -41,27 +19,13 @@ export default defineConfig(() => {
root: __dirname,
}),
],
worker: {
format: 'iife',
rollupOptions: {
output: {
inlineDynamicImports: true,
},
},
plugins: () => [
{
name: 'define-process-env',
transform: (code: string) =>
code
.replace(/process\.env\.NODE_ENV/g, JSON.stringify('production'))
.replace(/process\.env/g, '{}'),
},
],
},
build: {
emptyOutDir: false,
outDir: 'dist',
lib: { entry: entries, name: 'twenty-sdk' },
lib: {
entry: ['src/ui/index.ts', 'src/front-component-renderer/index.ts'],
name: 'twenty-sdk',
},
rollupOptions: {
onwarn: (warning, warn) => {
if (
@@ -82,14 +46,44 @@ export default defineConfig(() => {
output: [
{
format: 'es',
entryFileNames: (chunk) => entryFileNames(chunk, 'mjs'),
entryFileNames: (chunk) => {
if (
chunk.name === 'index' &&
chunk.facadeModuleId?.includes('ui/index.ts')
) {
return 'ui/index.mjs';
}
if (
chunk.facadeModuleId?.includes(
'front-component-renderer/index.ts',
)
) {
return 'front-component-renderer.mjs';
}
return '[name].mjs';
},
},
{
format: 'cjs',
interop: 'auto',
esModule: true,
exports: 'named',
entryFileNames: (chunk) => entryFileNames(chunk, 'cjs'),
entryFileNames: (chunk) => {
if (
chunk.name === 'index' &&
chunk.facadeModuleId?.includes('ui/index.ts')
) {
return 'ui/index.cjs';
}
if (
chunk.facadeModuleId?.includes(
'front-component-renderer/index.ts',
)
) {
return 'front-component-renderer.cjs';
}
return '[name].cjs';
},
},
],
},
+2 -1
View File
@@ -27,7 +27,8 @@ export default defineConfig(() => {
index: 'src/sdk/index.ts',
cli: 'src/cli/cli.ts',
operations: 'src/cli/operations/index.ts',
build: 'src/build/index.ts',
'front-component-renderer/build':
'src/front-component-renderer/build/index.ts',
},
name: 'twenty-sdk',
},
+88 -29
View File
@@ -5004,6 +5004,15 @@ __metadata:
languageName: node
linkType: hard
"@emotion/is-prop-valid@npm:1.4.0, @emotion/is-prop-valid@npm:^1.2.0, @emotion/is-prop-valid@npm:^1.3.0, @emotion/is-prop-valid@npm:^1.4.0":
version: 1.4.0
resolution: "@emotion/is-prop-valid@npm:1.4.0"
dependencies:
"@emotion/memoize": "npm:^0.9.0"
checksum: 10c0/5f857814ec7d8c7e727727346dfb001af6b1fb31d621a3ce9c3edf944a484d8b0d619546c30899ae3ade2f317c76390ba4394449728e9bf628312defc2c41ac3
languageName: node
linkType: hard
"@emotion/is-prop-valid@npm:^0.8.2":
version: 0.8.8
resolution: "@emotion/is-prop-valid@npm:0.8.8"
@@ -5013,15 +5022,6 @@ __metadata:
languageName: node
linkType: hard
"@emotion/is-prop-valid@npm:^1.2.0, @emotion/is-prop-valid@npm:^1.3.0, @emotion/is-prop-valid@npm:^1.4.0":
version: 1.4.0
resolution: "@emotion/is-prop-valid@npm:1.4.0"
dependencies:
"@emotion/memoize": "npm:^0.9.0"
checksum: 10c0/5f857814ec7d8c7e727727346dfb001af6b1fb31d621a3ce9c3edf944a484d8b0d619546c30899ae3ade2f317c76390ba4394449728e9bf628312defc2c41ac3
languageName: node
linkType: hard
"@emotion/memoize@npm:0.7.4":
version: 0.7.4
resolution: "@emotion/memoize@npm:0.7.4"
@@ -5104,6 +5104,13 @@ __metadata:
languageName: node
linkType: hard
"@emotion/unitless@npm:0.10.0, @emotion/unitless@npm:^0.10.0":
version: 0.10.0
resolution: "@emotion/unitless@npm:0.10.0"
checksum: 10c0/150943192727b7650eb9a6851a98034ddb58a8b6958b37546080f794696141c3760966ac695ab9af97efe10178690987aee4791f9f0ad1ff76783cdca83c1d49
languageName: node
linkType: hard
"@emotion/unitless@npm:0.8.1":
version: 0.8.1
resolution: "@emotion/unitless@npm:0.8.1"
@@ -5111,13 +5118,6 @@ __metadata:
languageName: node
linkType: hard
"@emotion/unitless@npm:^0.10.0":
version: 0.10.0
resolution: "@emotion/unitless@npm:0.10.0"
checksum: 10c0/150943192727b7650eb9a6851a98034ddb58a8b6958b37546080f794696141c3760966ac695ab9af97efe10178690987aee4791f9f0ad1ff76783cdca83c1d49
languageName: node
linkType: hard
"@emotion/use-insertion-effect-with-fallbacks@npm:^1.2.0":
version: 1.2.0
resolution: "@emotion/use-insertion-effect-with-fallbacks@npm:1.2.0"
@@ -25846,6 +25846,13 @@ __metadata:
languageName: node
linkType: hard
"@types/stylis@npm:4.2.7":
version: 4.2.7
resolution: "@types/stylis@npm:4.2.7"
checksum: 10c0/01a9679addb3f63951a9c09729564e2205581f2db40875a28b25cc461efc52ba17a711cc50cdb5e7d3a67c5f2cd60580e078c8a69b8df7b67699d89060d2a977
languageName: node
linkType: hard
"@types/superagent@npm:*":
version: 8.1.8
resolution: "@types/superagent@npm:8.1.8"
@@ -57965,6 +57972,29 @@ __metadata:
languageName: node
linkType: hard
"styled-components@npm:^6.1.0":
version: 6.3.12
resolution: "styled-components@npm:6.3.12"
dependencies:
"@emotion/is-prop-valid": "npm:1.4.0"
"@emotion/unitless": "npm:0.10.0"
"@types/stylis": "npm:4.2.7"
css-to-react-native: "npm:3.2.0"
csstype: "npm:3.2.3"
postcss: "npm:8.4.49"
shallowequal: "npm:1.1.0"
stylis: "npm:4.3.6"
tslib: "npm:2.8.1"
peerDependencies:
react: ">= 16.8.0"
react-dom: ">= 16.8.0"
peerDependenciesMeta:
react-dom:
optional: true
checksum: 10c0/1d8cb4182a55f9b94a813b8f4d662ae13f8bfc86da2deb672a9758aebe88fa5bba46241b58cdaff7b9a205197f144d4f041a753b8d020b1ebda77046970b9264
languageName: node
linkType: hard
"styled-components@npm:^6.1.11":
version: 6.1.15
resolution: "styled-components@npm:6.1.15"
@@ -58031,6 +58061,13 @@ __metadata:
languageName: node
linkType: hard
"stylis@npm:4.3.6":
version: 4.3.6
resolution: "stylis@npm:4.3.6"
checksum: 10c0/e736d484983a34f7c65d362c67dc79b7bce388054b261c2b7b23d02eaaf280617033f65d44b1ea341854f4331a5074b885668ac8741f98c13a6cfd6443ae85d0
languageName: node
linkType: hard
"subarg@npm:^1.0.0":
version: 1.0.0
resolution: "subarg@npm:1.0.0"
@@ -59566,6 +59603,40 @@ __metadata:
languageName: unknown
linkType: soft
"twenty-front-component-renderer@workspace:*, twenty-front-component-renderer@workspace:packages/twenty-front-component-renderer":
version: 0.0.0-use.local
resolution: "twenty-front-component-renderer@workspace:packages/twenty-front-component-renderer"
dependencies:
"@chakra-ui/react": "npm:^3.33.0"
"@emotion/react": "npm:^11.14.0"
"@emotion/styled": "npm:^11.14.0"
"@mui/material": "npm:^7.3.8"
"@quilted/threads": "npm:^4.0.1"
"@remote-dom/core": "npm:^1.10.1"
"@remote-dom/react": "npm:^1.2.2"
"@sniptt/guards": "npm:^0.2.0"
"@storybook/addon-vitest": "npm:^10.2.13"
"@storybook/react-vite": "npm:^10.2.13"
"@types/node": "npm:^24.0.0"
"@types/react": "npm:^19.0.0"
"@types/react-dom": "npm:^19.0.0"
"@vitest/browser-playwright": "npm:^4.0.18"
playwright: "npm:^1.56.1"
react: "npm:^18.2.0"
react-dom: "npm:^18.2.0"
storybook: "npm:^10.2.13"
styled-components: "npm:^6.1.0"
ts-morph: "npm:^25.0.0"
tsx: "npm:^4.7.0"
twenty-sdk: "workspace:*"
twenty-shared: "workspace:*"
twenty-ui: "workspace:*"
vite-plugin-dts: "npm:^4.5.4"
vite-tsconfig-paths: "npm:^4.2.1"
zod: "npm:^4.1.11"
languageName: unknown
linkType: soft
"twenty-front@workspace:packages/twenty-front":
version: 0.0.0-use.local
resolution: "twenty-front@workspace:packages/twenty-front"
@@ -59672,7 +59743,7 @@ __metadata:
remark-gfm: "npm:^4.0.1"
rollup-plugin-visualizer: "npm:^5.14.0"
transliteration: "npm:^2.3.5"
twenty-sdk: "workspace:*"
twenty-front-component-renderer: "workspace:*"
twenty-shared: "workspace:*"
twenty-ui: "workspace:*"
use-debounce: "npm:^10.0.0"
@@ -59691,24 +59762,14 @@ __metadata:
version: 0.0.0-use.local
resolution: "twenty-sdk@workspace:packages/twenty-sdk"
dependencies:
"@chakra-ui/react": "npm:^3.33.0"
"@emotion/react": "npm:^11.14.0"
"@emotion/styled": "npm:^11.14.0"
"@genql/cli": "npm:^3.0.3"
"@genql/runtime": "npm:^2.10.0"
"@mui/material": "npm:^7.3.8"
"@prettier/sync": "npm:^0.5.2"
"@quilted/threads": "npm:^4.0.1"
"@remote-dom/core": "npm:^1.10.1"
"@remote-dom/react": "npm:^1.2.2"
"@sniptt/guards": "npm:^0.2.0"
"@storybook/addon-vitest": "npm:^10.2.13"
"@storybook/react-vite": "npm:^10.2.13"
"@types/inquirer": "npm:^9.0.0"
"@types/node": "npm:^24.0.0"
"@types/react": "npm:^19.0.0"
"@types/react-dom": "npm:^19.0.0"
"@vitest/browser-playwright": "npm:^4.0.18"
axios: "npm:^1.13.5"
chalk: "npm:^5.3.0"
chokidar: "npm:^4.0.0"
@@ -59720,11 +59781,9 @@ __metadata:
ink: "npm:^6.8.0"
inquirer: "npm:^10.0.0"
jsonc-parser: "npm:^3.2.0"
playwright: "npm:^1.56.1"
preact: "npm:^10.28.3"
react: "npm:^19.0.0"
react-dom: "npm:^19.0.0"
storybook: "npm:^10.2.13"
tinyglobby: "npm:^0.2.15"
ts-morph: "npm:^25.0.0"
tsx: "npm:^4.7.0"