Front components communication between host and remote (#17716)
Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
+8
-1
@@ -1,10 +1,13 @@
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { getMockFrontComponentUrl } from '@/front-components/utils/mockFrontComponent';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useState } from 'react';
|
||||
import { FrontComponentRenderer as SharedFrontComponentRenderer } from 'twenty-sdk/front-component';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { FrontComponentRenderer as SharedFrontComponentRenderer } from 'twenty-sdk/front-component/renderer';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
type FrontComponentRendererProps = {
|
||||
frontComponentId: string;
|
||||
};
|
||||
@@ -16,6 +19,7 @@ export const FrontComponentRenderer = ({
|
||||
const [hasError, setHasError] = useState(false);
|
||||
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
|
||||
const handleError = (error?: Error) => {
|
||||
if (isDefined(error)) {
|
||||
@@ -36,6 +40,9 @@ export const FrontComponentRenderer = ({
|
||||
<SharedFrontComponentRenderer
|
||||
theme={theme}
|
||||
componentUrl={getMockFrontComponentUrl()}
|
||||
executionContext={{
|
||||
userId: currentUser?.id,
|
||||
}}
|
||||
onError={handleError}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,18 +1,107 @@
|
||||
/* eslint-disable */
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
const mockFrontComponentCode = `
|
||||
const { jsx } = globalThis;
|
||||
const { TwentyUiButton } = globalThis.RemoteComponents;
|
||||
// react-globals:react
|
||||
var useState = globalThis.React.useState;
|
||||
var useEffect = globalThis.React.useEffect;
|
||||
var useSyncExternalStore = globalThis.React.useSyncExternalStore;
|
||||
|
||||
export default jsx(TwentyUiButton, {
|
||||
variant: 'primary',
|
||||
disabled: false,
|
||||
fullWidth: false,
|
||||
onClick: () => {
|
||||
console.log('Button clicked');
|
||||
},
|
||||
title: 'Click me',
|
||||
});
|
||||
// react-globals:react/jsx-runtime
|
||||
var jsx = globalThis.jsx;
|
||||
var jsxs = globalThis.jsxs;
|
||||
var Fragment = globalThis.React.Fragment;
|
||||
|
||||
// src/tata/test-component.front-component.tsx
|
||||
var RemoteComponents = globalThis.RemoteComponents;
|
||||
var getStore = () => {
|
||||
const store = globalThis.frontComponentExecutionContextStore;
|
||||
if (store === void 0) {
|
||||
throw new Error(
|
||||
"frontComponentExecutionContextStore not found on globalThis. This hook must be used within a front component running in the worker."
|
||||
);
|
||||
}
|
||||
return store;
|
||||
};
|
||||
var useFrontComponentExecutionContext = () => {
|
||||
const store = getStore();
|
||||
return useSyncExternalStore(store.subscribe, store.getSnapshot);
|
||||
};
|
||||
var TestComponent = () => {
|
||||
const [count, setCount] = useState(0);
|
||||
const [timer, setTimer] = useState(0);
|
||||
const context = useFrontComponentExecutionContext();
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setTimer((prevTimer) => prevTimer + 1);
|
||||
}, 1e3);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
return /* @__PURE__ */ jsxs(
|
||||
RemoteComponents.HtmlDiv,
|
||||
{
|
||||
style: {
|
||||
padding: "20px",
|
||||
fontFamily: "Arial, sans-serif",
|
||||
maxWidth: "400px",
|
||||
margin: "0 auto",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center"
|
||||
},
|
||||
children: [
|
||||
/* @__PURE__ */ jsx(RemoteComponents.HtmlH1, { style: { color: "#333", marginBottom: "20px", fontSize: "24px" }, children: "Test Component" }),
|
||||
/* @__PURE__ */ jsxs(RemoteComponents.HtmlP, { style: { fontSize: "18px", marginBottom: "10px", color: "#666" }, children: [
|
||||
"Count: ",
|
||||
count
|
||||
] }),
|
||||
/* @__PURE__ */ jsxs(RemoteComponents.HtmlP, { style: { fontSize: "18px", marginBottom: "20px", color: "#666" }, children: [
|
||||
"Timer: ",
|
||||
timer,
|
||||
"s"
|
||||
] }),
|
||||
/* @__PURE__ */ jsxs(RemoteComponents.HtmlP, { style: { fontSize: "18px", marginBottom: "20px", color: "#666" }, children: [
|
||||
"User ID: ",
|
||||
context?.userId
|
||||
] }),
|
||||
/* @__PURE__ */ jsx(
|
||||
RemoteComponents.HtmlButton,
|
||||
{
|
||||
onClick: () => setCount(count + 1),
|
||||
style: {
|
||||
padding: "10px 20px",
|
||||
fontSize: "16px",
|
||||
backgroundColor: "#007bff",
|
||||
color: "white",
|
||||
border: "none",
|
||||
borderRadius: "5px",
|
||||
cursor: "pointer",
|
||||
transition: "all 0.3s ease",
|
||||
boxShadow: "0 2px 4px rgba(0, 0, 0, 0.2)"
|
||||
},
|
||||
onMouseEnter: (e) => {
|
||||
e.currentTarget.style.backgroundColor = "#0056b3";
|
||||
e.currentTarget.style.transform = "translateY(-2px)";
|
||||
e.currentTarget.style.boxShadow = "0 4px 8px rgba(0, 0, 0, 0.3)";
|
||||
},
|
||||
onMouseLeave: (e) => {
|
||||
e.currentTarget.style.backgroundColor = "#007bff";
|
||||
e.currentTarget.style.transform = "translateY(0)";
|
||||
e.currentTarget.style.boxShadow = "0 2px 4px rgba(0, 0, 0, 0.2)";
|
||||
},
|
||||
children: "Increment"
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
);
|
||||
};
|
||||
var test_component_front_component_default = globalThis.jsx(TestComponent, {});
|
||||
export {
|
||||
test_component_front_component_default as default,
|
||||
useFrontComponentExecutionContext
|
||||
};
|
||||
//# sourceMappingURL=test-component.front-component.mjs.map
|
||||
`;
|
||||
|
||||
let cachedMockBlobUrl: string | null = null;
|
||||
|
||||
@@ -5,13 +5,12 @@
|
||||
"module": "dist/index.mjs",
|
||||
"types": "dist/index.d.ts",
|
||||
"bin": {
|
||||
"twenty": "dist/cli/index.cjs"
|
||||
"twenty": "dist/cli.cjs"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"front-component",
|
||||
"front-component-constants",
|
||||
"ui"
|
||||
"ui",
|
||||
"front-component"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "npx rimraf dist && npx vite build",
|
||||
@@ -32,20 +31,20 @@
|
||||
"import": "./dist/index.mjs",
|
||||
"require": "./dist/index.cjs"
|
||||
},
|
||||
"./front-component": {
|
||||
"types": "./dist/front-component/index.d.ts",
|
||||
"import": "./dist/front-component/index.mjs",
|
||||
"require": "./dist/front-component/index.cjs"
|
||||
},
|
||||
"./front-component-constants": {
|
||||
"types": "./dist/front-component-constants/index.d.ts",
|
||||
"import": "./dist/front-component-constants/index.mjs",
|
||||
"require": "./dist/front-component-constants/index.cjs"
|
||||
},
|
||||
"./ui": {
|
||||
"types": "./dist/ui/index.d.ts",
|
||||
"import": "./dist/ui/index.mjs",
|
||||
"require": "./dist/ui/index.cjs"
|
||||
},
|
||||
"./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"
|
||||
},
|
||||
"./front-component/api": {
|
||||
"types": "./dist/front-component/api/index.d.ts",
|
||||
"import": "./dist/front-component/api/index.mjs",
|
||||
"require": "./dist/front-component/api/index.cjs"
|
||||
}
|
||||
},
|
||||
"license": "AGPL-3.0",
|
||||
@@ -105,14 +104,14 @@
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"front-component": [
|
||||
"dist/front-component/index.d.ts"
|
||||
],
|
||||
"front-component-constants": [
|
||||
"dist/front-component-constants/index.d.ts"
|
||||
],
|
||||
"ui": [
|
||||
"dist/ui/index.d.ts"
|
||||
],
|
||||
"front-component/renderer": [
|
||||
"dist/front-component/renderer/index.d.ts"
|
||||
],
|
||||
"front-component/api": [
|
||||
"dist/front-component/api/index.d.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,32 +3,15 @@
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "packages/twenty-sdk/src",
|
||||
"projectType": "library",
|
||||
"tags": [
|
||||
"scope:sdk",
|
||||
"scope:shared"
|
||||
],
|
||||
"tags": ["scope:sdk", "scope:shared"],
|
||||
"targets": {
|
||||
"build": {
|
||||
"dependsOn": [
|
||||
"generateBarrels",
|
||||
"^build"
|
||||
],
|
||||
"outputs": [
|
||||
"{projectRoot}/dist",
|
||||
"{projectRoot}/front-component/package.json",
|
||||
"{projectRoot}/front-component/dist",
|
||||
"{projectRoot}/front-component-constants/package.json",
|
||||
"{projectRoot}/front-component-constants/dist",
|
||||
"{projectRoot}/ui/package.json",
|
||||
"{projectRoot}/ui/dist"
|
||||
]
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": ["{projectRoot}/dist"]
|
||||
},
|
||||
"dev": {
|
||||
"executor": "nx:run-commands",
|
||||
"dependsOn": [
|
||||
"generateBarrels",
|
||||
"^build"
|
||||
],
|
||||
"dependsOn": ["^build"],
|
||||
"options": {
|
||||
"cwd": "packages/twenty-sdk",
|
||||
"command": "npx rimraf dist && npx vite build --watch"
|
||||
@@ -36,43 +19,21 @@
|
||||
},
|
||||
"start": {
|
||||
"executor": "nx:run-commands",
|
||||
"dependsOn": [
|
||||
"build"
|
||||
],
|
||||
"dependsOn": ["build"],
|
||||
"options": {
|
||||
"cwd": "packages/twenty-sdk",
|
||||
"command": "node dist/cli.cjs"
|
||||
}
|
||||
},
|
||||
"generateBarrels": {
|
||||
"executor": "nx:run-commands",
|
||||
"cache": true,
|
||||
"inputs": [
|
||||
"production",
|
||||
"{projectRoot}/scripts/generateBarrels.ts"
|
||||
],
|
||||
"outputs": [
|
||||
"{projectRoot}/src/index.ts",
|
||||
"{projectRoot}/src/*/index.ts",
|
||||
"{projectRoot}/package.json"
|
||||
],
|
||||
"options": {
|
||||
"command": "tsx {projectRoot}/scripts/generateBarrels.ts"
|
||||
}
|
||||
},
|
||||
"typecheck": {},
|
||||
"lint": {
|
||||
"options": {
|
||||
"lintFilePatterns": [
|
||||
"{projectRoot}/src/**/*.{ts,json}"
|
||||
],
|
||||
"lintFilePatterns": ["{projectRoot}/src/**/*.{ts,json}"],
|
||||
"maxWarnings": 0
|
||||
},
|
||||
"configurations": {
|
||||
"ci": {
|
||||
"lintFilePatterns": [
|
||||
"{projectRoot}/src/**/*.{ts,json}"
|
||||
],
|
||||
"lintFilePatterns": ["{projectRoot}/src/**/*.{ts,json}"],
|
||||
"maxWarnings": 0
|
||||
},
|
||||
"fix": {}
|
||||
@@ -80,9 +41,7 @@
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nx/vitest:test",
|
||||
"outputs": [
|
||||
"{workspaceRoot}/coverage/{projectRoot}"
|
||||
],
|
||||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
||||
"options": {
|
||||
"config": "{projectRoot}/vitest.config.ts"
|
||||
},
|
||||
@@ -146,9 +105,7 @@
|
||||
"storybook:prebuild": {
|
||||
"executor": "nx:run-commands",
|
||||
"cache": true,
|
||||
"dependsOn": [
|
||||
"generateRemoteDomElements"
|
||||
],
|
||||
"dependsOn": ["generateRemoteDomElements"],
|
||||
"inputs": [
|
||||
"{projectRoot}/src/front-component/__stories__/mocks/**/*",
|
||||
"{projectRoot}/src/front-component/__stories__/utils/**/*",
|
||||
@@ -156,25 +113,19 @@
|
||||
"{projectRoot}/src/front-component-constants/**/*",
|
||||
"{projectRoot}/src/sdk/**/*"
|
||||
],
|
||||
"outputs": [
|
||||
"{projectRoot}/src/front-component/__stories__/built/*"
|
||||
],
|
||||
"outputs": ["{projectRoot}/src/front-component/__stories__/built/*"],
|
||||
"options": {
|
||||
"command": "tsx {projectRoot}/src/front-component/__stories__/utils/buildMockComponents.ts"
|
||||
}
|
||||
},
|
||||
"storybook:build": {
|
||||
"dependsOn": [
|
||||
"storybook:prebuild"
|
||||
],
|
||||
"dependsOn": ["storybook:prebuild"],
|
||||
"configurations": {
|
||||
"test": {}
|
||||
}
|
||||
},
|
||||
"storybook:serve:dev": {
|
||||
"dependsOn": [
|
||||
"storybook:prebuild"
|
||||
],
|
||||
"dependsOn": ["storybook:prebuild"],
|
||||
"options": {
|
||||
"port": 6008
|
||||
}
|
||||
@@ -189,17 +140,13 @@
|
||||
}
|
||||
},
|
||||
"storybook:test": {
|
||||
"dependsOn": [
|
||||
"storybook:prebuild"
|
||||
],
|
||||
"dependsOn": ["storybook:prebuild"],
|
||||
"options": {
|
||||
"command": "vitest run --coverage --config vitest.storybook.config.ts --shard={args.shard}"
|
||||
}
|
||||
},
|
||||
"storybook:test:no-coverage": {
|
||||
"dependsOn": [
|
||||
"storybook:prebuild"
|
||||
],
|
||||
"dependsOn": ["storybook:prebuild"],
|
||||
"options": {
|
||||
"command": "vitest run --config vitest.storybook.config.ts --shard={args.shard}"
|
||||
}
|
||||
|
||||
@@ -1,559 +0,0 @@
|
||||
import prettier from '@prettier/sync';
|
||||
import * as fs from 'fs';
|
||||
import { globSync } from 'glob';
|
||||
import path from 'path';
|
||||
import { type Options } from 'prettier';
|
||||
import slash from 'slash';
|
||||
import ts from 'typescript';
|
||||
|
||||
// TODO prastoin refactor this file in several one into its dedicated package and make it a TypeScript CLI
|
||||
|
||||
const INDEX_FILENAME = 'index';
|
||||
const PACKAGE_JSON_FILENAME = 'package.json';
|
||||
const NX_PROJECT_CONFIGURATION_FILENAME = 'project.json';
|
||||
const PACKAGE_PATH = path.resolve('packages/twenty-sdk');
|
||||
const SRC_PATH = path.resolve(`${PACKAGE_PATH}/src`);
|
||||
const PACKAGE_JSON_PATH = path.join(PACKAGE_PATH, PACKAGE_JSON_FILENAME);
|
||||
const NX_PROJECT_CONFIGURATION_PATH = path.join(
|
||||
PACKAGE_PATH,
|
||||
NX_PROJECT_CONFIGURATION_FILENAME,
|
||||
);
|
||||
const EXCLUDED_EXTENSIONS = [
|
||||
'**/*.test.ts',
|
||||
'**/*.test.tsx',
|
||||
'**/*.spec.ts',
|
||||
'**/*.spec.tsx',
|
||||
'**/*.stories.ts',
|
||||
'**/*.stories.tsx',
|
||||
] as const;
|
||||
const EXCLUDED_DIRECTORIES = [
|
||||
'**/__tests__/**',
|
||||
'**/__mocks__/**',
|
||||
'**/__stories__/**',
|
||||
'**/internal/**',
|
||||
'**/cli/**',
|
||||
] as const;
|
||||
const ROOT_DIRECTORIES = ['sdk'];
|
||||
|
||||
// Modules that re-export from external packages instead of local files
|
||||
const EXTERNAL_REEXPORT_MODULES: Record<string, string> = {
|
||||
ui: `export * from 'twenty-ui';
|
||||
export * from 'twenty-ui/accessibility';
|
||||
export * from 'twenty-ui/components';
|
||||
export * from 'twenty-ui/display';
|
||||
export * from 'twenty-ui/feedback';
|
||||
export * from 'twenty-ui/input';
|
||||
export * from 'twenty-ui/json-visualizer';
|
||||
export * from 'twenty-ui/layout';
|
||||
export * from 'twenty-ui/navigation';
|
||||
export * from 'twenty-ui/theme';
|
||||
export * from 'twenty-ui/utilities';`,
|
||||
};
|
||||
|
||||
const prettierConfigFile = prettier.resolveConfigFile();
|
||||
if (prettierConfigFile == null) {
|
||||
throw new Error('Prettier config file not found');
|
||||
}
|
||||
const prettierConfiguration = prettier.resolveConfig(prettierConfigFile);
|
||||
const prettierFormat = (str: string, parser: Options['parser']) =>
|
||||
prettier.format(str, {
|
||||
...prettierConfiguration,
|
||||
parser,
|
||||
});
|
||||
type createTypeScriptFileArgs = {
|
||||
path: string;
|
||||
content: string;
|
||||
filename: string;
|
||||
};
|
||||
const createTypeScriptFile = ({
|
||||
content,
|
||||
path: filePath,
|
||||
filename,
|
||||
}: createTypeScriptFileArgs) => {
|
||||
const header = `
|
||||
/*
|
||||
* _____ _
|
||||
*|_ _|_ _____ _ __ | |_ _ _
|
||||
* | | \\ \\ /\\ / / _ \\ '_ \\| __| | | | Auto-generated file
|
||||
* | | \\ V V / __/ | | | |_| |_| | Any edits to this will be overridden
|
||||
* |_| \\_/\\_/ \\___|_| |_|\\__|\\__, |
|
||||
* |___/
|
||||
*/
|
||||
`;
|
||||
const formattedContent = prettierFormat(
|
||||
`${header}\n${content}\n`,
|
||||
'typescript',
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(filePath, `${filename}.ts`),
|
||||
formattedContent,
|
||||
'utf-8',
|
||||
);
|
||||
};
|
||||
|
||||
const getLastPathFolder = (pathStr: string) => path.basename(pathStr);
|
||||
|
||||
const getSubDirectoryPaths = (directoryPath: string): string[] => {
|
||||
const pattern = slash(path.join(directoryPath, '*/'));
|
||||
return globSync(pattern, {
|
||||
ignore: [...EXCLUDED_DIRECTORIES],
|
||||
cwd: SRC_PATH,
|
||||
nodir: false,
|
||||
maxDepth: 1,
|
||||
}).sort((a, b) => a.localeCompare(b));
|
||||
};
|
||||
|
||||
const partitionFileExportsByType = (declarations: DeclarationOccurrence[]) => {
|
||||
return declarations.reduce<{
|
||||
typeAndInterfaceDeclarations: DeclarationOccurrence[];
|
||||
otherDeclarations: DeclarationOccurrence[];
|
||||
}>(
|
||||
(acc, { kind, name }) => {
|
||||
if (kind === 'type' || kind === 'interface') {
|
||||
return {
|
||||
...acc,
|
||||
typeAndInterfaceDeclarations: [
|
||||
...acc.typeAndInterfaceDeclarations,
|
||||
{ kind, name },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...acc,
|
||||
otherDeclarations: [...acc.otherDeclarations, { kind, name }],
|
||||
};
|
||||
},
|
||||
{
|
||||
typeAndInterfaceDeclarations: [],
|
||||
otherDeclarations: [],
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const generateModuleIndexFiles = (exportByBarrel: ExportByBarrel[]) => {
|
||||
return exportByBarrel.map<createTypeScriptFileArgs>(
|
||||
({ barrel: { moduleDirectory }, allFileExports }) => {
|
||||
const content = allFileExports
|
||||
.sort((a, b) => a.file.localeCompare(b.file))
|
||||
.map(({ exports, file }) => {
|
||||
const { otherDeclarations, typeAndInterfaceDeclarations } =
|
||||
partitionFileExportsByType(exports);
|
||||
|
||||
const fileWithoutExtension = path.parse(file).name;
|
||||
const pathToImport = slash(
|
||||
path.relative(
|
||||
moduleDirectory,
|
||||
path.join(path.dirname(file), fileWithoutExtension),
|
||||
),
|
||||
);
|
||||
const mapDeclarationNameAndJoin = (
|
||||
declarations: DeclarationOccurrence[],
|
||||
) => declarations.map(({ name }) => name).join(', ');
|
||||
|
||||
const typeExport =
|
||||
typeAndInterfaceDeclarations.length > 0
|
||||
? `export type { ${mapDeclarationNameAndJoin(typeAndInterfaceDeclarations)} } from "./${pathToImport}"`
|
||||
: '';
|
||||
const othersExport =
|
||||
otherDeclarations.length > 0
|
||||
? `export { ${mapDeclarationNameAndJoin(otherDeclarations)} } from "./${pathToImport}"`
|
||||
: '';
|
||||
|
||||
return [typeExport, othersExport]
|
||||
.filter((el) => el !== '')
|
||||
.join('\n');
|
||||
})
|
||||
.join('\n');
|
||||
|
||||
return {
|
||||
content,
|
||||
path: moduleDirectory,
|
||||
filename: INDEX_FILENAME,
|
||||
};
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
type JsonUpdate = Record<string, any>;
|
||||
type WriteInJsonFileArgs = {
|
||||
content: JsonUpdate;
|
||||
file: string;
|
||||
};
|
||||
const updateJsonFile = ({ content, file }: WriteInJsonFileArgs) => {
|
||||
const updatedJsonFile = JSON.stringify(content);
|
||||
const formattedContent = prettierFormat(updatedJsonFile, 'json-stringify');
|
||||
fs.writeFileSync(file, formattedContent, 'utf-8');
|
||||
};
|
||||
|
||||
const writeInPackageJson = (update: JsonUpdate) => {
|
||||
const rawJsonFile = fs.readFileSync(PACKAGE_JSON_PATH, 'utf-8');
|
||||
const initialJsonFile = JSON.parse(rawJsonFile);
|
||||
|
||||
updateJsonFile({
|
||||
file: PACKAGE_JSON_PATH,
|
||||
content: {
|
||||
...initialJsonFile,
|
||||
...update,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const updateNxProjectConfigurationBuildOutputs = (outputs: JsonUpdate) => {
|
||||
const rawJsonFile = fs.readFileSync(NX_PROJECT_CONFIGURATION_PATH, 'utf-8');
|
||||
const initialJsonFile = JSON.parse(rawJsonFile);
|
||||
|
||||
updateJsonFile({
|
||||
file: NX_PROJECT_CONFIGURATION_PATH,
|
||||
content: {
|
||||
...initialJsonFile,
|
||||
targets: {
|
||||
...initialJsonFile.targets,
|
||||
build: {
|
||||
...initialJsonFile.targets.build,
|
||||
outputs,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
type ExportOccurrence = {
|
||||
types: string;
|
||||
import: string;
|
||||
require: string;
|
||||
};
|
||||
type ExportsConfig = Record<string, ExportOccurrence | string>;
|
||||
|
||||
const generateModulePackageExports = (moduleDirectories: string[]) => {
|
||||
return moduleDirectories.reduce<ExportsConfig>((acc, moduleDirectory) => {
|
||||
const moduleName = getLastPathFolder(moduleDirectory);
|
||||
if (moduleName === undefined) {
|
||||
throw new Error(
|
||||
`Should never occur, moduleName is undefined ${moduleDirectory}`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
...acc,
|
||||
[`./${moduleName}`]: {
|
||||
types: `./dist/${moduleName}/index.d.ts`,
|
||||
import: `./dist/${moduleName}/index.mjs`,
|
||||
require: `./dist/${moduleName}/index.cjs`,
|
||||
},
|
||||
};
|
||||
}, {});
|
||||
};
|
||||
|
||||
const computePackageJsonFilesAndExportsConfig = (
|
||||
moduleDirectories: string[],
|
||||
) => {
|
||||
const entrypoints = moduleDirectories.map(getLastPathFolder);
|
||||
const exports = {
|
||||
'.': {
|
||||
types: './dist/index.d.ts',
|
||||
import: './dist/index.mjs',
|
||||
require: './dist/index.cjs',
|
||||
},
|
||||
...generateModulePackageExports(moduleDirectories),
|
||||
} satisfies ExportsConfig;
|
||||
|
||||
const typesVersionsEntries = entrypoints.reduce<Record<string, string[]>>(
|
||||
(acc, moduleName) => ({
|
||||
...acc,
|
||||
[`${moduleName}`]: [`dist/${moduleName}/index.d.ts`],
|
||||
}),
|
||||
{},
|
||||
);
|
||||
|
||||
return {
|
||||
exports,
|
||||
typesVersions: { '*': typesVersionsEntries },
|
||||
files: ['dist', ...entrypoints],
|
||||
};
|
||||
};
|
||||
|
||||
const computeProjectNxBuildOutputsPath = (moduleDirectories: string[]) => {
|
||||
const dynamicOutputsPath = moduleDirectories
|
||||
.map(getLastPathFolder)
|
||||
.flatMap((barrelName) =>
|
||||
['package.json', 'dist'].map(
|
||||
(subPath) => `{projectRoot}/${barrelName}/${subPath}`,
|
||||
),
|
||||
);
|
||||
|
||||
return ['{projectRoot}/dist', ...dynamicOutputsPath];
|
||||
};
|
||||
|
||||
const getTypeScriptFiles = (
|
||||
directoryPath: string,
|
||||
includeIndex: boolean = false,
|
||||
): string[] => {
|
||||
const pattern = slash(path.join(directoryPath, '**', '*.{ts,tsx}'));
|
||||
const files = globSync(pattern, {
|
||||
cwd: SRC_PATH,
|
||||
nodir: true,
|
||||
ignore: [...EXCLUDED_EXTENSIONS, ...EXCLUDED_DIRECTORIES],
|
||||
});
|
||||
|
||||
return files.filter(
|
||||
(file) =>
|
||||
!file.endsWith('.d.ts') &&
|
||||
(includeIndex ? true : !file.endsWith('index.ts')),
|
||||
);
|
||||
};
|
||||
|
||||
const getKind = (
|
||||
node: ts.VariableStatement,
|
||||
): Extract<ExportKind, 'const' | 'let' | 'var'> => {
|
||||
const isConst = (node.declarationList.flags & ts.NodeFlags.Const) !== 0;
|
||||
if (isConst) {
|
||||
return 'const';
|
||||
}
|
||||
|
||||
const isLet = (node.declarationList.flags & ts.NodeFlags.Let) !== 0;
|
||||
if (isLet) {
|
||||
return 'let';
|
||||
}
|
||||
|
||||
return 'var';
|
||||
};
|
||||
|
||||
const extractExportsFromSourceFile = (sourceFile: ts.SourceFile) => {
|
||||
const exports: DeclarationOccurrence[] = [];
|
||||
|
||||
const visit = (node: ts.Node) => {
|
||||
if (!ts.canHaveModifiers(node)) {
|
||||
ts.forEachChild(node, visit);
|
||||
return;
|
||||
}
|
||||
const modifiers = ts.getModifiers(node);
|
||||
const isExport = modifiers?.some(
|
||||
(mod) => mod.kind === ts.SyntaxKind.ExportKeyword,
|
||||
);
|
||||
|
||||
if (!isExport && !ts.isExportDeclaration(node)) {
|
||||
ts.forEachChild(node, visit);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (true) {
|
||||
case ts.isTypeAliasDeclaration(node):
|
||||
exports.push({
|
||||
kind: 'type',
|
||||
name: node.name.text,
|
||||
});
|
||||
break;
|
||||
|
||||
case ts.isInterfaceDeclaration(node):
|
||||
exports.push({
|
||||
kind: 'interface',
|
||||
name: node.name.text,
|
||||
});
|
||||
break;
|
||||
|
||||
case ts.isEnumDeclaration(node):
|
||||
exports.push({
|
||||
kind: 'enum',
|
||||
name: node.name.text,
|
||||
});
|
||||
break;
|
||||
|
||||
case ts.isFunctionDeclaration(node) && node.name !== undefined:
|
||||
exports.push({
|
||||
kind: 'function',
|
||||
name: node.name.text,
|
||||
});
|
||||
break;
|
||||
|
||||
case ts.isVariableStatement(node):
|
||||
node.declarationList.declarations.forEach((decl) => {
|
||||
const kind = getKind(node);
|
||||
|
||||
if (ts.isIdentifier(decl.name)) {
|
||||
exports.push({
|
||||
kind,
|
||||
name: decl.name.text,
|
||||
});
|
||||
} else if (ts.isObjectBindingPattern(decl.name)) {
|
||||
decl.name.elements.forEach((element) => {
|
||||
if (
|
||||
!ts.isBindingElement(element) ||
|
||||
!ts.isIdentifier(element.name)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
exports.push({
|
||||
kind,
|
||||
name: element.name.text,
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case ts.isClassDeclaration(node) && node.name !== undefined:
|
||||
exports.push({
|
||||
kind: 'class',
|
||||
name: node.name.text,
|
||||
});
|
||||
break;
|
||||
case ts.isExportDeclaration(node):
|
||||
if (node.exportClause && ts.isNamedExports(node.exportClause)) {
|
||||
node.exportClause.elements.forEach((element) => {
|
||||
const exportName = element.name.text;
|
||||
|
||||
// Check both the declaration and the individual specifier for type-only exports
|
||||
const isTypeExport =
|
||||
node.isTypeOnly || ts.isTypeOnlyExportDeclaration(node);
|
||||
if (isTypeExport) {
|
||||
// should handle kind
|
||||
exports.push({
|
||||
kind: 'type',
|
||||
name: exportName,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
exports.push({
|
||||
kind: 'const',
|
||||
name: exportName,
|
||||
});
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
ts.forEachChild(node, visit);
|
||||
};
|
||||
|
||||
visit(sourceFile);
|
||||
return exports;
|
||||
};
|
||||
|
||||
type ExportKind =
|
||||
| 'type'
|
||||
| 'interface'
|
||||
| 'enum'
|
||||
| 'function'
|
||||
| 'const'
|
||||
| 'let'
|
||||
| 'var'
|
||||
| 'class';
|
||||
type DeclarationOccurrence = { kind: ExportKind; name: string };
|
||||
type FileExports = Array<{
|
||||
file: string;
|
||||
exports: DeclarationOccurrence[];
|
||||
}>;
|
||||
|
||||
const findAllExports = (directoryPath: string): FileExports => {
|
||||
const results: FileExports = [];
|
||||
|
||||
const files = getTypeScriptFiles(directoryPath);
|
||||
|
||||
for (const file of files) {
|
||||
const sourceFile = ts.createSourceFile(
|
||||
file,
|
||||
fs.readFileSync(file, 'utf8'),
|
||||
ts.ScriptTarget.Latest,
|
||||
true,
|
||||
);
|
||||
|
||||
const exports = extractExportsFromSourceFile(sourceFile);
|
||||
if (exports.length > 0) {
|
||||
results.push({
|
||||
file,
|
||||
exports,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
type ExportByBarrel = {
|
||||
barrel: {
|
||||
moduleName: string;
|
||||
moduleDirectory: string;
|
||||
};
|
||||
allFileExports: FileExports;
|
||||
};
|
||||
const retrieveExportsByBarrel = (barrelDirectories: string[]) => {
|
||||
return barrelDirectories.map<ExportByBarrel>((moduleDirectory) => {
|
||||
const moduleExportsPerFile = findAllExports(moduleDirectory);
|
||||
const moduleName = getLastPathFolder(moduleDirectory);
|
||||
if (!moduleName) {
|
||||
throw new Error(
|
||||
`Should never occur moduleName not found ${moduleDirectory}`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
barrel: {
|
||||
moduleName,
|
||||
moduleDirectory,
|
||||
},
|
||||
allFileExports: moduleExportsPerFile,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const main = () => {
|
||||
const moduleDirectories = getSubDirectoryPaths(SRC_PATH);
|
||||
|
||||
const rootDirectory = moduleDirectories.find((dir) =>
|
||||
ROOT_DIRECTORIES.includes(getLastPathFolder(dir)),
|
||||
);
|
||||
|
||||
// Separate modules that re-export from external packages
|
||||
const externalReexportModuleNames = Object.keys(EXTERNAL_REEXPORT_MODULES);
|
||||
const localModuleDirectories = moduleDirectories.filter(
|
||||
(dir) => !externalReexportModuleNames.includes(getLastPathFolder(dir)),
|
||||
);
|
||||
|
||||
const otherBarrelDirectories = moduleDirectories.filter(
|
||||
(dir) => !ROOT_DIRECTORIES.includes(getLastPathFolder(dir)),
|
||||
);
|
||||
|
||||
const exportsByBarrel = retrieveExportsByBarrel(localModuleDirectories);
|
||||
const moduleIndexFiles = generateModuleIndexFiles(exportsByBarrel);
|
||||
|
||||
const packageJsonConfig = computePackageJsonFilesAndExportsConfig(
|
||||
otherBarrelDirectories,
|
||||
);
|
||||
const nxBuildOutputsPath = computeProjectNxBuildOutputsPath(
|
||||
otherBarrelDirectories,
|
||||
);
|
||||
|
||||
updateNxProjectConfigurationBuildOutputs(nxBuildOutputsPath);
|
||||
writeInPackageJson(packageJsonConfig);
|
||||
moduleIndexFiles.forEach(createTypeScriptFile);
|
||||
|
||||
// Generate index files for modules that re-export from external packages
|
||||
for (const [moduleName, content] of Object.entries(
|
||||
EXTERNAL_REEXPORT_MODULES,
|
||||
)) {
|
||||
const moduleDirectory = path.join(SRC_PATH, moduleName);
|
||||
if (!fs.existsSync(moduleDirectory)) {
|
||||
fs.mkdirSync(moduleDirectory, { recursive: true });
|
||||
}
|
||||
createTypeScriptFile({
|
||||
path: moduleDirectory,
|
||||
filename: INDEX_FILENAME,
|
||||
content,
|
||||
});
|
||||
}
|
||||
|
||||
// Ensure top-level src/index.ts re-exports the root directories barrel so consumers can `import * from "twenty-sdk"`
|
||||
// We intentionally keep this file minimal: it delegates to the generated src/<rootDirectory>/index.ts
|
||||
if (rootDirectory) {
|
||||
createTypeScriptFile({
|
||||
path: SRC_PATH,
|
||||
filename: INDEX_FILENAME,
|
||||
content: ROOT_DIRECTORIES.map(
|
||||
(rootDirectory) => `export * from "./${rootDirectory}";`,
|
||||
).join('\n'),
|
||||
});
|
||||
}
|
||||
};
|
||||
main();
|
||||
@@ -3,11 +3,11 @@ import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { IndentationText, Project, QuoteKind } from 'ts-morph';
|
||||
|
||||
import { ALLOWED_HTML_ELEMENTS } from '../../src/front-component-constants/AllowedHtmlElements';
|
||||
import { ALLOWED_UI_COMPONENTS } from '../../src/front-component-constants/AllowedUiComponents';
|
||||
import { COMMON_HTML_EVENTS } from '../../src/front-component-constants/CommonHtmlEvents';
|
||||
import { EVENT_TO_REACT } from '../../src/front-component-constants/EventToReact';
|
||||
import { HTML_COMMON_PROPERTIES } from '../../src/front-component-constants/HtmlCommonProperties';
|
||||
import { ALLOWED_HTML_ELEMENTS } from '../../src/front-component/common/AllowedHtmlElements';
|
||||
import { ALLOWED_UI_COMPONENTS } from '../../src/front-component/common/AllowedUiComponents';
|
||||
import { COMMON_HTML_EVENTS } from '../../src/front-component/common/CommonHtmlEvents';
|
||||
import { EVENT_TO_REACT } from '../../src/front-component/common/EventToReact';
|
||||
import { HTML_COMMON_PROPERTIES } from '../../src/front-component/common/HtmlCommonProperties';
|
||||
|
||||
import {
|
||||
type ComponentSchema,
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { HTML_TAG_TO_REMOTE_COMPONENT } from '../../../../../../front-component-constants';
|
||||
import { HTML_TAG_TO_REMOTE_COMPONENT } from '../../../../../../front-component/common';
|
||||
|
||||
const REMOTE_COMPONENTS_GLOBAL_NAMESPACE = 'RemoteComponents';
|
||||
|
||||
|
||||
@@ -6,11 +6,12 @@ import {
|
||||
} from '@/cli/utilities/build/manifest/manifest-extract-config';
|
||||
import { extractManifestFromFile } from '@/cli/utilities/build/manifest/manifest-extract-config-from-file';
|
||||
import {
|
||||
type ApplicationConfig,
|
||||
type FrontComponentConfig,
|
||||
type LogicFunctionConfig,
|
||||
type ApplicationConfig,
|
||||
} from '@/sdk';
|
||||
import { glob } from 'fast-glob';
|
||||
import * as fs from 'fs-extra';
|
||||
import { readFile } from 'fs-extra';
|
||||
import { basename, extname, relative, sep } from 'path';
|
||||
import {
|
||||
@@ -24,9 +25,8 @@ import {
|
||||
type ObjectManifest,
|
||||
type RoleManifest,
|
||||
} from 'twenty-shared/application';
|
||||
import { assertUnreachable } from 'twenty-shared/utils';
|
||||
import { type Sources } from 'twenty-shared/types';
|
||||
import * as fs from 'fs-extra';
|
||||
import { assertUnreachable } from 'twenty-shared/utils';
|
||||
|
||||
const loadSources = async (appPath: string): Promise<string[]> => {
|
||||
return await glob(['**/*.ts', '**/*.tsx'], {
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
||||
import { expect, fn, userEvent, waitFor, within } from 'storybook/test';
|
||||
|
||||
import { FrontComponentRenderer } from '../host/components/FrontComponentRenderer';
|
||||
import { FrontComponentRenderer } from '../renderer/host/components/FrontComponentRenderer';
|
||||
|
||||
import { getBuiltComponentPath } from './utils/loadBuiltComponent';
|
||||
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
import { useSyncExternalStore } from 'react';
|
||||
import { type FrontComponentExecutionContext } from '../types/FrontComponentExecutionContext';
|
||||
import { type FrontComponentExecutionContextStore } from '../types/FrontComponentExecutionContextStore';
|
||||
|
||||
const getStore = (): FrontComponentExecutionContextStore => {
|
||||
const store = (globalThis as Record<string, unknown>)
|
||||
.frontComponentExecutionContextStore as
|
||||
| FrontComponentExecutionContextStore
|
||||
| undefined;
|
||||
|
||||
if (store === undefined) {
|
||||
throw new Error(
|
||||
'frontComponentExecutionContextStore not found on globalThis. This hook must be used within a front component running in the worker.',
|
||||
);
|
||||
}
|
||||
|
||||
return store;
|
||||
};
|
||||
|
||||
export const useFrontComponentExecutionContext = ():
|
||||
| FrontComponentExecutionContext
|
||||
| undefined => {
|
||||
const store = getStore();
|
||||
|
||||
return useSyncExternalStore(store.subscribe, store.getSnapshot);
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export { useFrontComponentExecutionContext } from './hooks/useFrontComponentExecutionContext';
|
||||
export type { FrontComponentExecutionContext } from './types/FrontComponentExecutionContext';
|
||||
export type { FrontComponentExecutionContextStore } from './types/FrontComponentExecutionContextStore';
|
||||
@@ -0,0 +1,3 @@
|
||||
export type FrontComponentExecutionContext = {
|
||||
userId: string;
|
||||
};
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import { type FrontComponentExecutionContext } from './FrontComponentExecutionContext';
|
||||
|
||||
export type FrontComponentExecutionContextStore = {
|
||||
getSnapshot: () => FrontComponentExecutionContext | undefined;
|
||||
subscribe: (listener: () => void) => () => void;
|
||||
};
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { type PropertySchema } from '../front-component/types/PropertySchema';
|
||||
import { type PropertySchema } from '../renderer/types/PropertySchema';
|
||||
|
||||
export type AllowedUiComponent = {
|
||||
tag: string;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { type PropertySchema } from '../front-component/types/PropertySchema';
|
||||
import { type PropertySchema } from '../renderer/types/PropertySchema';
|
||||
|
||||
export const HTML_COMMON_PROPERTIES: Record<string, PropertySchema> = {
|
||||
id: { type: 'string', optional: true },
|
||||
@@ -1,44 +0,0 @@
|
||||
import {
|
||||
type RemoteReceiver,
|
||||
RemoteRootRenderer,
|
||||
} from '@remote-dom/react/host';
|
||||
import { useState } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { ThemeProvider } from '@emotion/react';
|
||||
import { type ThemeType } from 'twenty-ui/theme';
|
||||
import { FrontComponentWorkerEffect } from '../../remote/components/FrontComponentWorkerEffect';
|
||||
import { componentRegistry } from '../generated/host-component-registry';
|
||||
|
||||
type FrontComponentContentProps = {
|
||||
componentUrl: string;
|
||||
onError: (error?: Error) => void;
|
||||
theme: ThemeType;
|
||||
};
|
||||
|
||||
export const FrontComponentRenderer = ({
|
||||
componentUrl,
|
||||
onError,
|
||||
theme,
|
||||
}: FrontComponentContentProps) => {
|
||||
const [receiver, setReceiver] = useState<RemoteReceiver | null>(null);
|
||||
|
||||
return (
|
||||
<>
|
||||
<FrontComponentWorkerEffect
|
||||
componentUrl={componentUrl}
|
||||
setReceiver={setReceiver}
|
||||
onError={onError}
|
||||
/>
|
||||
|
||||
{isDefined(receiver) && (
|
||||
<ThemeProvider theme={theme}>
|
||||
<RemoteRootRenderer
|
||||
receiver={receiver}
|
||||
components={componentRegistry}
|
||||
/>
|
||||
</ThemeProvider>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
import { FrontComponentErrorEffect } from '@/front-component/renderer/remote/components/FrontComponentErrorEffect';
|
||||
import { FrontComponentUpdateContextEffect } from '@/front-component/renderer/remote/components/FrontComponentUpdateContextEffect';
|
||||
import { type FrontComponentExecutionContext } from '@/front-component/renderer/types/FrontComponentExecutionContext';
|
||||
import { type WorkerExports } from '@/front-component/renderer/types/WorkerExports';
|
||||
import { type ThreadWebWorker } from '@quilted/threads';
|
||||
import {
|
||||
type RemoteReceiver,
|
||||
RemoteRootRenderer,
|
||||
} from '@remote-dom/react/host';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { ThemeProvider } from '@emotion/react';
|
||||
import { type ThemeType } from 'twenty-ui/theme';
|
||||
import { FrontComponentWorkerEffect } from '../../remote/components/FrontComponentWorkerEffect';
|
||||
import { componentRegistry } from '../generated/host-component-registry';
|
||||
|
||||
type FrontComponentContentProps = {
|
||||
componentUrl: string;
|
||||
executionContext: FrontComponentExecutionContext;
|
||||
onError: (error?: Error) => void;
|
||||
theme: ThemeType;
|
||||
};
|
||||
|
||||
export const FrontComponentRenderer = ({
|
||||
componentUrl,
|
||||
executionContext,
|
||||
onError,
|
||||
theme,
|
||||
}: FrontComponentContentProps) => {
|
||||
const [receiver, setReceiver] = useState<RemoteReceiver | null>(null);
|
||||
const [thread, setThread] = useState<ThreadWebWorker<WorkerExports> | null>(
|
||||
null,
|
||||
);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
const MemoizedFrontComponentWorkerEffect = useMemo(() => {
|
||||
return (
|
||||
<FrontComponentWorkerEffect
|
||||
componentUrl={componentUrl}
|
||||
setReceiver={setReceiver}
|
||||
setThread={setThread}
|
||||
setError={setError}
|
||||
/>
|
||||
);
|
||||
}, [componentUrl, setError, setReceiver, setThread]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{MemoizedFrontComponentWorkerEffect}
|
||||
|
||||
{isDefined(error) && (
|
||||
<FrontComponentErrorEffect error={error} onError={onError} />
|
||||
)}
|
||||
|
||||
{isDefined(thread) && (
|
||||
<FrontComponentUpdateContextEffect
|
||||
thread={thread}
|
||||
executionContext={executionContext}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isDefined(receiver) && (
|
||||
<ThemeProvider theme={theme}>
|
||||
<RemoteRootRenderer
|
||||
receiver={receiver}
|
||||
components={componentRegistry}
|
||||
/>
|
||||
</ThemeProvider>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
+405
@@ -0,0 +1,405 @@
|
||||
/*
|
||||
* _____ _
|
||||
*|_ _|_ _____ _ __ | |_ _ _
|
||||
* | | \ \ /\ / / _ \ '_ \| __| | | | Auto-generated file
|
||||
* | | \ V V / __/ | | | |_| |_| | Any edits to this will be overridden
|
||||
* |_| \_/\_/ \___|_| |_|\__|\__, |
|
||||
* |___/
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
RemoteFragmentRenderer,
|
||||
createRemoteComponentRenderer,
|
||||
} from '@remote-dom/react/host';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
const INTERNAL_PROPS = new Set(['element', 'receiver', 'components']);
|
||||
|
||||
const EVENT_NAME_MAP: Record<string, string> = {
|
||||
onclick: 'onClick',
|
||||
ondblclick: 'onDoubleClick',
|
||||
onmousedown: 'onMouseDown',
|
||||
onmouseup: 'onMouseUp',
|
||||
onmouseover: 'onMouseOver',
|
||||
onmouseout: 'onMouseOut',
|
||||
onmouseenter: 'onMouseEnter',
|
||||
onmouseleave: 'onMouseLeave',
|
||||
onkeydown: 'onKeyDown',
|
||||
onkeyup: 'onKeyUp',
|
||||
onkeypress: 'onKeyPress',
|
||||
onfocus: 'onFocus',
|
||||
onblur: 'onBlur',
|
||||
onchange: 'onChange',
|
||||
oninput: 'onInput',
|
||||
onsubmit: 'onSubmit',
|
||||
onscroll: 'onScroll',
|
||||
onwheel: 'onWheel',
|
||||
oncontextmenu: 'onContextMenu',
|
||||
ondrag: 'onDrag',
|
||||
};
|
||||
|
||||
const parseStyle = (
|
||||
styleString: string | undefined,
|
||||
): React.CSSProperties | undefined => {
|
||||
if (!styleString || typeof styleString !== 'string') {
|
||||
return styleString as React.CSSProperties | undefined;
|
||||
}
|
||||
|
||||
const style: Record<string, string> = {};
|
||||
const declarations = styleString.split(';').filter(Boolean);
|
||||
|
||||
for (const declaration of declarations) {
|
||||
const colonIndex = declaration.indexOf(':');
|
||||
if (colonIndex === -1) continue;
|
||||
|
||||
const property = declaration.slice(0, colonIndex).trim();
|
||||
const value = declaration.slice(colonIndex + 1).trim();
|
||||
|
||||
const camelProperty = property.replace(/-([a-z])/g, (_, letter: string) =>
|
||||
letter.toUpperCase(),
|
||||
);
|
||||
style[camelProperty] = value;
|
||||
}
|
||||
|
||||
return style;
|
||||
};
|
||||
|
||||
const wrapEventHandler = (handler: () => void) => {
|
||||
return (_event: unknown) => {
|
||||
handler();
|
||||
};
|
||||
};
|
||||
|
||||
const filterProps = (props: Record<string, unknown>) => {
|
||||
const filtered: Record<string, unknown> = {};
|
||||
for (const [key, value] of Object.entries(props)) {
|
||||
if (INTERNAL_PROPS.has(key) || value === undefined) continue;
|
||||
|
||||
if (key === 'style') {
|
||||
filtered.style = parseStyle(value as string | undefined);
|
||||
} else {
|
||||
const normalizedKey = EVENT_NAME_MAP[key.toLowerCase()] || key;
|
||||
if (normalizedKey.startsWith('on') && typeof value === 'function') {
|
||||
filtered[normalizedKey] = wrapEventHandler(value as () => void);
|
||||
} else {
|
||||
filtered[normalizedKey] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return filtered;
|
||||
};
|
||||
const HtmlDivWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('div', filterProps(props), children);
|
||||
};
|
||||
const HtmlSpanWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('span', filterProps(props), children);
|
||||
};
|
||||
const HtmlSectionWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('section', filterProps(props), children);
|
||||
};
|
||||
const HtmlArticleWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('article', filterProps(props), children);
|
||||
};
|
||||
const HtmlHeaderWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('header', filterProps(props), children);
|
||||
};
|
||||
const HtmlFooterWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('footer', filterProps(props), children);
|
||||
};
|
||||
const HtmlMainWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('main', filterProps(props), children);
|
||||
};
|
||||
const HtmlNavWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('nav', filterProps(props), children);
|
||||
};
|
||||
const HtmlAsideWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('aside', filterProps(props), children);
|
||||
};
|
||||
const HtmlPWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('p', filterProps(props), children);
|
||||
};
|
||||
const HtmlH1Wrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('h1', filterProps(props), children);
|
||||
};
|
||||
const HtmlH2Wrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('h2', filterProps(props), children);
|
||||
};
|
||||
const HtmlH3Wrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('h3', filterProps(props), children);
|
||||
};
|
||||
const HtmlH4Wrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('h4', filterProps(props), children);
|
||||
};
|
||||
const HtmlH5Wrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('h5', filterProps(props), children);
|
||||
};
|
||||
const HtmlH6Wrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('h6', filterProps(props), children);
|
||||
};
|
||||
const HtmlStrongWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('strong', filterProps(props), children);
|
||||
};
|
||||
const HtmlEmWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('em', filterProps(props), children);
|
||||
};
|
||||
const HtmlSmallWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('small', filterProps(props), children);
|
||||
};
|
||||
const HtmlCodeWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('code', filterProps(props), children);
|
||||
};
|
||||
const HtmlPreWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('pre', filterProps(props), children);
|
||||
};
|
||||
const HtmlBlockquoteWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('blockquote', filterProps(props), children);
|
||||
};
|
||||
const HtmlAWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('a', filterProps(props), children);
|
||||
};
|
||||
const HtmlImgWrapper = ({
|
||||
children: _children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('img', filterProps(props));
|
||||
};
|
||||
const HtmlUlWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('ul', filterProps(props), children);
|
||||
};
|
||||
const HtmlOlWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('ol', filterProps(props), children);
|
||||
};
|
||||
const HtmlLiWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('li', filterProps(props), children);
|
||||
};
|
||||
const HtmlFormWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('form', filterProps(props), children);
|
||||
};
|
||||
const HtmlLabelWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('label', filterProps(props), children);
|
||||
};
|
||||
const HtmlInputWrapper = ({
|
||||
children: _children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('input', filterProps(props));
|
||||
};
|
||||
const HtmlTextareaWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('textarea', filterProps(props), children);
|
||||
};
|
||||
const HtmlSelectWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('select', filterProps(props), children);
|
||||
};
|
||||
const HtmlOptionWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('option', filterProps(props), children);
|
||||
};
|
||||
const HtmlButtonWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('button', filterProps(props), children);
|
||||
};
|
||||
const HtmlTableWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('table', filterProps(props), children);
|
||||
};
|
||||
const HtmlTheadWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('thead', filterProps(props), children);
|
||||
};
|
||||
const HtmlTbodyWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('tbody', filterProps(props), children);
|
||||
};
|
||||
const HtmlTfootWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('tfoot', filterProps(props), children);
|
||||
};
|
||||
const HtmlTrWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('tr', filterProps(props), children);
|
||||
};
|
||||
const HtmlThWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('th', filterProps(props), children);
|
||||
};
|
||||
const HtmlTdWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('td', filterProps(props), children);
|
||||
};
|
||||
const HtmlBrWrapper = ({
|
||||
children: _children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('br', filterProps(props));
|
||||
};
|
||||
const HtmlHrWrapper = ({
|
||||
children: _children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('hr', filterProps(props));
|
||||
};
|
||||
const TwentyUiButtonWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement(Button, filterProps(props), children);
|
||||
};
|
||||
type ComponentRegistryValue =
|
||||
| ReturnType<typeof createRemoteComponentRenderer>
|
||||
| typeof RemoteFragmentRenderer;
|
||||
|
||||
export const componentRegistry: Map<string, ComponentRegistryValue> = new Map([
|
||||
['html-div', createRemoteComponentRenderer(HtmlDivWrapper)],
|
||||
['html-span', createRemoteComponentRenderer(HtmlSpanWrapper)],
|
||||
['html-section', createRemoteComponentRenderer(HtmlSectionWrapper)],
|
||||
['html-article', createRemoteComponentRenderer(HtmlArticleWrapper)],
|
||||
['html-header', createRemoteComponentRenderer(HtmlHeaderWrapper)],
|
||||
['html-footer', createRemoteComponentRenderer(HtmlFooterWrapper)],
|
||||
['html-main', createRemoteComponentRenderer(HtmlMainWrapper)],
|
||||
['html-nav', createRemoteComponentRenderer(HtmlNavWrapper)],
|
||||
['html-aside', createRemoteComponentRenderer(HtmlAsideWrapper)],
|
||||
['html-p', createRemoteComponentRenderer(HtmlPWrapper)],
|
||||
['html-h1', createRemoteComponentRenderer(HtmlH1Wrapper)],
|
||||
['html-h2', createRemoteComponentRenderer(HtmlH2Wrapper)],
|
||||
['html-h3', createRemoteComponentRenderer(HtmlH3Wrapper)],
|
||||
['html-h4', createRemoteComponentRenderer(HtmlH4Wrapper)],
|
||||
['html-h5', createRemoteComponentRenderer(HtmlH5Wrapper)],
|
||||
['html-h6', createRemoteComponentRenderer(HtmlH6Wrapper)],
|
||||
['html-strong', createRemoteComponentRenderer(HtmlStrongWrapper)],
|
||||
['html-em', createRemoteComponentRenderer(HtmlEmWrapper)],
|
||||
['html-small', createRemoteComponentRenderer(HtmlSmallWrapper)],
|
||||
['html-code', createRemoteComponentRenderer(HtmlCodeWrapper)],
|
||||
['html-pre', createRemoteComponentRenderer(HtmlPreWrapper)],
|
||||
['html-blockquote', createRemoteComponentRenderer(HtmlBlockquoteWrapper)],
|
||||
['html-a', createRemoteComponentRenderer(HtmlAWrapper)],
|
||||
['html-img', createRemoteComponentRenderer(HtmlImgWrapper)],
|
||||
['html-ul', createRemoteComponentRenderer(HtmlUlWrapper)],
|
||||
['html-ol', createRemoteComponentRenderer(HtmlOlWrapper)],
|
||||
['html-li', createRemoteComponentRenderer(HtmlLiWrapper)],
|
||||
['html-form', createRemoteComponentRenderer(HtmlFormWrapper)],
|
||||
['html-label', createRemoteComponentRenderer(HtmlLabelWrapper)],
|
||||
['html-input', createRemoteComponentRenderer(HtmlInputWrapper)],
|
||||
['html-textarea', createRemoteComponentRenderer(HtmlTextareaWrapper)],
|
||||
['html-select', createRemoteComponentRenderer(HtmlSelectWrapper)],
|
||||
['html-option', createRemoteComponentRenderer(HtmlOptionWrapper)],
|
||||
['html-button', createRemoteComponentRenderer(HtmlButtonWrapper)],
|
||||
['html-table', createRemoteComponentRenderer(HtmlTableWrapper)],
|
||||
['html-thead', createRemoteComponentRenderer(HtmlTheadWrapper)],
|
||||
['html-tbody', createRemoteComponentRenderer(HtmlTbodyWrapper)],
|
||||
['html-tfoot', createRemoteComponentRenderer(HtmlTfootWrapper)],
|
||||
['html-tr', createRemoteComponentRenderer(HtmlTrWrapper)],
|
||||
['html-th', createRemoteComponentRenderer(HtmlThWrapper)],
|
||||
['html-td', createRemoteComponentRenderer(HtmlTdWrapper)],
|
||||
['html-br', createRemoteComponentRenderer(HtmlBrWrapper)],
|
||||
['html-hr', createRemoteComponentRenderer(HtmlHrWrapper)],
|
||||
['twenty-ui-button', createRemoteComponentRenderer(TwentyUiButtonWrapper)],
|
||||
['remote-fragment', RemoteFragmentRenderer],
|
||||
]);
|
||||
+93
-95
@@ -1,126 +1,124 @@
|
||||
/*
|
||||
* _____ _
|
||||
*|_ _|_ _____ _ __ | |_ _ _
|
||||
* | | \ \ /\ / / _ \ '_ \| __| | | | Auto-generated file
|
||||
* | | \ V V / __/ | | | |_| |_| | Any edits to this will be overridden
|
||||
* |_| \_/\_/ \___|_| |_|\__|\__, |
|
||||
* |___/
|
||||
*/
|
||||
|
||||
export { FrontComponentRenderer } from './host/components/FrontComponentRenderer';
|
||||
export { componentRegistry } from './host/generated/host-component-registry';
|
||||
export { FrontComponentErrorEffect } from './remote/components/FrontComponentErrorEffect';
|
||||
export { FrontComponentUpdateContextEffect } from './remote/components/FrontComponentUpdateContextEffect';
|
||||
export { FrontComponentWorkerEffect } from './remote/components/FrontComponentWorkerEffect';
|
||||
export {
|
||||
HtmlDiv,
|
||||
HtmlSpan,
|
||||
HtmlSection,
|
||||
FrontComponentExecutionContextStore,
|
||||
frontComponentExecutionContextStore,
|
||||
} from './remote/context/FrontComponentExecutionContextStore';
|
||||
export {
|
||||
HtmlA,
|
||||
HtmlArticle,
|
||||
HtmlHeader,
|
||||
HtmlFooter,
|
||||
HtmlMain,
|
||||
HtmlNav,
|
||||
HtmlAside,
|
||||
HtmlP,
|
||||
HtmlBlockquote,
|
||||
HtmlBr,
|
||||
HtmlButton,
|
||||
HtmlCode,
|
||||
HtmlDiv,
|
||||
HtmlEm,
|
||||
HtmlFooter,
|
||||
HtmlForm,
|
||||
HtmlH1,
|
||||
HtmlH2,
|
||||
HtmlH3,
|
||||
HtmlH4,
|
||||
HtmlH5,
|
||||
HtmlH6,
|
||||
HtmlStrong,
|
||||
HtmlEm,
|
||||
HtmlSmall,
|
||||
HtmlCode,
|
||||
HtmlPre,
|
||||
HtmlBlockquote,
|
||||
HtmlA,
|
||||
HtmlImg,
|
||||
HtmlUl,
|
||||
HtmlOl,
|
||||
HtmlLi,
|
||||
HtmlForm,
|
||||
HtmlLabel,
|
||||
HtmlInput,
|
||||
HtmlTextarea,
|
||||
HtmlSelect,
|
||||
HtmlOption,
|
||||
HtmlButton,
|
||||
HtmlTable,
|
||||
HtmlThead,
|
||||
HtmlTbody,
|
||||
HtmlTfoot,
|
||||
HtmlTr,
|
||||
HtmlTh,
|
||||
HtmlTd,
|
||||
HtmlBr,
|
||||
HtmlHeader,
|
||||
HtmlHr,
|
||||
HtmlImg,
|
||||
HtmlInput,
|
||||
HtmlLabel,
|
||||
HtmlLi,
|
||||
HtmlMain,
|
||||
HtmlNav,
|
||||
HtmlOl,
|
||||
HtmlOption,
|
||||
HtmlP,
|
||||
HtmlPre,
|
||||
HtmlSection,
|
||||
HtmlSelect,
|
||||
HtmlSmall,
|
||||
HtmlSpan,
|
||||
HtmlStrong,
|
||||
HtmlTable,
|
||||
HtmlTbody,
|
||||
HtmlTd,
|
||||
HtmlTextarea,
|
||||
HtmlTfoot,
|
||||
HtmlTh,
|
||||
HtmlThead,
|
||||
HtmlTr,
|
||||
HtmlUl,
|
||||
TwentyUiButton,
|
||||
} from './remote/generated/remote-components';
|
||||
export type {
|
||||
HtmlCommonProperties,
|
||||
HtmlCommonEvents,
|
||||
HtmlAProperties,
|
||||
HtmlImgProperties,
|
||||
HtmlFormProperties,
|
||||
HtmlLabelProperties,
|
||||
HtmlInputProperties,
|
||||
HtmlTextareaProperties,
|
||||
HtmlSelectProperties,
|
||||
HtmlOptionProperties,
|
||||
HtmlButtonProperties,
|
||||
HtmlThProperties,
|
||||
HtmlTdProperties,
|
||||
TwentyUiButtonProperties,
|
||||
} from './remote/generated/remote-elements';
|
||||
export {
|
||||
HtmlDivElement,
|
||||
HtmlSpanElement,
|
||||
HtmlSectionElement,
|
||||
HtmlAElement,
|
||||
HtmlArticleElement,
|
||||
HtmlHeaderElement,
|
||||
HtmlFooterElement,
|
||||
HtmlMainElement,
|
||||
HtmlNavElement,
|
||||
HtmlAsideElement,
|
||||
HtmlPElement,
|
||||
HtmlBlockquoteElement,
|
||||
HtmlBrElement,
|
||||
HtmlButtonElement,
|
||||
HtmlCodeElement,
|
||||
HtmlDivElement,
|
||||
HtmlEmElement,
|
||||
HtmlFooterElement,
|
||||
HtmlFormElement,
|
||||
HtmlH1Element,
|
||||
HtmlH2Element,
|
||||
HtmlH3Element,
|
||||
HtmlH4Element,
|
||||
HtmlH5Element,
|
||||
HtmlH6Element,
|
||||
HtmlStrongElement,
|
||||
HtmlEmElement,
|
||||
HtmlSmallElement,
|
||||
HtmlCodeElement,
|
||||
HtmlPreElement,
|
||||
HtmlBlockquoteElement,
|
||||
HtmlAElement,
|
||||
HtmlImgElement,
|
||||
HtmlUlElement,
|
||||
HtmlOlElement,
|
||||
HtmlLiElement,
|
||||
HtmlFormElement,
|
||||
HtmlLabelElement,
|
||||
HtmlInputElement,
|
||||
HtmlTextareaElement,
|
||||
HtmlSelectElement,
|
||||
HtmlOptionElement,
|
||||
HtmlButtonElement,
|
||||
HtmlTableElement,
|
||||
HtmlTheadElement,
|
||||
HtmlTbodyElement,
|
||||
HtmlTfootElement,
|
||||
HtmlTrElement,
|
||||
HtmlThElement,
|
||||
HtmlTdElement,
|
||||
HtmlBrElement,
|
||||
HtmlHeaderElement,
|
||||
HtmlHrElement,
|
||||
TwentyUiButtonElement,
|
||||
RemoteRootElement,
|
||||
HtmlImgElement,
|
||||
HtmlInputElement,
|
||||
HtmlLabelElement,
|
||||
HtmlLiElement,
|
||||
HtmlMainElement,
|
||||
HtmlNavElement,
|
||||
HtmlOlElement,
|
||||
HtmlOptionElement,
|
||||
HtmlPElement,
|
||||
HtmlPreElement,
|
||||
HtmlSectionElement,
|
||||
HtmlSelectElement,
|
||||
HtmlSmallElement,
|
||||
HtmlSpanElement,
|
||||
HtmlStrongElement,
|
||||
HtmlTableElement,
|
||||
HtmlTbodyElement,
|
||||
HtmlTdElement,
|
||||
HtmlTextareaElement,
|
||||
HtmlTfootElement,
|
||||
HtmlThElement,
|
||||
HtmlTheadElement,
|
||||
HtmlTrElement,
|
||||
HtmlUlElement,
|
||||
RemoteFragmentElement,
|
||||
RemoteRootElement,
|
||||
TwentyUiButtonElement,
|
||||
} from './remote/generated/remote-elements';
|
||||
export type {
|
||||
HtmlAProperties,
|
||||
HtmlButtonProperties,
|
||||
HtmlCommonEvents,
|
||||
HtmlCommonProperties,
|
||||
HtmlFormProperties,
|
||||
HtmlImgProperties,
|
||||
HtmlInputProperties,
|
||||
HtmlLabelProperties,
|
||||
HtmlOptionProperties,
|
||||
HtmlSelectProperties,
|
||||
HtmlTdProperties,
|
||||
HtmlTextareaProperties,
|
||||
HtmlThProperties,
|
||||
TwentyUiButtonProperties,
|
||||
} from './remote/generated/remote-elements';
|
||||
export { createRemoteWorker } from './remote/worker/createRemoteWorker';
|
||||
export type { FrontComponentExecutionContext } from './types/FrontComponentExecutionContext';
|
||||
export type { HostToWorkerRenderContext } from './types/HostToWorkerRenderContext';
|
||||
export type { PropertySchema } from './types/PropertySchema';
|
||||
export type { WorkerExports } from './types/WorkerExports';
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
type FrontComponentErrorEffectProps = {
|
||||
error: Error | null;
|
||||
onError: (error: Error) => void;
|
||||
};
|
||||
export const FrontComponentErrorEffect = ({
|
||||
error,
|
||||
onError,
|
||||
}: FrontComponentErrorEffectProps) => {
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
onError(error);
|
||||
}
|
||||
}, [error, onError]);
|
||||
|
||||
return null;
|
||||
};
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import { type FrontComponentExecutionContext } from '@/front-component/renderer/types/FrontComponentExecutionContext';
|
||||
import { type WorkerExports } from '@/front-component/renderer/types/WorkerExports';
|
||||
import { type ThreadWebWorker } from '@quilted/threads';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
type FrontComponentUpdateContextEffectProps = {
|
||||
thread: ThreadWebWorker<WorkerExports>;
|
||||
executionContext: FrontComponentExecutionContext;
|
||||
};
|
||||
|
||||
export const FrontComponentUpdateContextEffect = ({
|
||||
thread,
|
||||
executionContext,
|
||||
}: FrontComponentUpdateContextEffectProps) => {
|
||||
useEffect(() => {
|
||||
thread.imports.updateContext(executionContext).catch(() => {});
|
||||
}, [executionContext, thread]);
|
||||
|
||||
return null;
|
||||
};
|
||||
+11
-5
@@ -7,13 +7,17 @@ import { createRemoteWorker } from '../worker/createRemoteWorker';
|
||||
type FrontComponentWorkerEffectProps = {
|
||||
componentUrl: string;
|
||||
setReceiver: React.Dispatch<React.SetStateAction<RemoteReceiver | null>>;
|
||||
onError: (error?: Error) => void;
|
||||
setThread: React.Dispatch<
|
||||
React.SetStateAction<ThreadWebWorker<WorkerExports> | null>
|
||||
>;
|
||||
setError: React.Dispatch<React.SetStateAction<Error | null>>;
|
||||
};
|
||||
|
||||
export const FrontComponentWorkerEffect = ({
|
||||
componentUrl,
|
||||
setReceiver,
|
||||
onError,
|
||||
setThread,
|
||||
setError,
|
||||
}: FrontComponentWorkerEffectProps) => {
|
||||
useEffect(() => {
|
||||
const newReceiver = new RemoteReceiver({ retain, release });
|
||||
@@ -21,23 +25,25 @@ export const FrontComponentWorkerEffect = ({
|
||||
const worker = createRemoteWorker();
|
||||
|
||||
worker.onerror = (event: ErrorEvent) => {
|
||||
onError(event.error);
|
||||
setError(event.error);
|
||||
};
|
||||
|
||||
const thread = new ThreadWebWorker<WorkerExports>(worker);
|
||||
setThread(thread);
|
||||
|
||||
thread.imports
|
||||
.render(newReceiver.connection, { componentUrl })
|
||||
.catch((error: Error) => {
|
||||
onError(error);
|
||||
setError(error);
|
||||
});
|
||||
|
||||
setReceiver(newReceiver);
|
||||
|
||||
return () => {
|
||||
setThread(null);
|
||||
worker.terminate();
|
||||
};
|
||||
}, [componentUrl, onError, setReceiver]);
|
||||
}, [componentUrl, setError, setReceiver, setThread]);
|
||||
|
||||
return null;
|
||||
};
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
import { type FrontComponentExecutionContext } from '../../types/FrontComponentExecutionContext';
|
||||
|
||||
type Listener = () => void;
|
||||
|
||||
export class FrontComponentExecutionContextStore {
|
||||
private context: FrontComponentExecutionContext | undefined = undefined;
|
||||
private listeners = new Set<Listener>();
|
||||
|
||||
getSnapshot = (): FrontComponentExecutionContext | undefined => {
|
||||
return this.context;
|
||||
};
|
||||
|
||||
subscribe = (listener: Listener): (() => void) => {
|
||||
this.listeners.add(listener);
|
||||
|
||||
return () => this.listeners.delete(listener);
|
||||
};
|
||||
|
||||
setContext = (context: FrontComponentExecutionContext): void => {
|
||||
this.context = context;
|
||||
for (const listener of this.listeners) {
|
||||
listener();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const frontComponentExecutionContextStore =
|
||||
new FrontComponentExecutionContextStore();
|
||||
+1157
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,733 @@
|
||||
/*
|
||||
* _____ _
|
||||
*|_ _|_ _____ _ __ | |_ _ _
|
||||
* | | \ \ /\ / / _ \ '_ \| __| | | | Auto-generated file
|
||||
* | | \ V V / __/ | | | |_| |_| | Any edits to this will be overridden
|
||||
* |_| \_/\_/ \___|_| |_|\__|\__, |
|
||||
* |___/
|
||||
*/
|
||||
|
||||
import {
|
||||
createRemoteElement,
|
||||
RemoteRootElement,
|
||||
RemoteFragmentElement,
|
||||
type RemoteEvent,
|
||||
} from '@remote-dom/core/elements';
|
||||
|
||||
export type HtmlCommonProperties = {
|
||||
id?: string;
|
||||
className?: string;
|
||||
style?: string;
|
||||
title?: string;
|
||||
tabIndex?: number;
|
||||
role?: string;
|
||||
'aria-label'?: string;
|
||||
'aria-hidden'?: boolean;
|
||||
'data-testid'?: string;
|
||||
};
|
||||
export type HtmlCommonEvents = {
|
||||
click(event: RemoteEvent): void;
|
||||
dblclick(event: RemoteEvent): void;
|
||||
mousedown(event: RemoteEvent): void;
|
||||
mouseup(event: RemoteEvent): void;
|
||||
mouseover(event: RemoteEvent): void;
|
||||
mouseout(event: RemoteEvent): void;
|
||||
mouseenter(event: RemoteEvent): void;
|
||||
mouseleave(event: RemoteEvent): void;
|
||||
keydown(event: RemoteEvent): void;
|
||||
keyup(event: RemoteEvent): void;
|
||||
keypress(event: RemoteEvent): void;
|
||||
focus(event: RemoteEvent): void;
|
||||
blur(event: RemoteEvent): void;
|
||||
change(event: RemoteEvent): void;
|
||||
input(event: RemoteEvent): void;
|
||||
submit(event: RemoteEvent): void;
|
||||
scroll(event: RemoteEvent): void;
|
||||
wheel(event: RemoteEvent): void;
|
||||
contextmenu(event: RemoteEvent): void;
|
||||
drag(event: RemoteEvent): void;
|
||||
};
|
||||
|
||||
const HTML_COMMON_EVENTS_ARRAY = [
|
||||
'click',
|
||||
'dblclick',
|
||||
'mousedown',
|
||||
'mouseup',
|
||||
'mouseover',
|
||||
'mouseout',
|
||||
'mouseenter',
|
||||
'mouseleave',
|
||||
'keydown',
|
||||
'keyup',
|
||||
'keypress',
|
||||
'focus',
|
||||
'blur',
|
||||
'change',
|
||||
'input',
|
||||
'submit',
|
||||
'scroll',
|
||||
'wheel',
|
||||
'contextmenu',
|
||||
'drag',
|
||||
] as const;
|
||||
const HTML_COMMON_PROPERTIES_CONFIG = {
|
||||
id: { type: String },
|
||||
className: { type: String },
|
||||
style: { type: String },
|
||||
title: { type: String },
|
||||
tabIndex: { type: Number },
|
||||
role: { type: String },
|
||||
'aria-label': { type: String },
|
||||
'aria-hidden': { type: Boolean },
|
||||
'data-testid': { type: String },
|
||||
};
|
||||
export const HtmlDivElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlSpanElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlSectionElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlArticleElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlHeaderElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlFooterElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlMainElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlNavElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlAsideElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlPElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlH1Element = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlH2Element = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlH3Element = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlH4Element = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlH5Element = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlH6Element = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlStrongElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlEmElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlSmallElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlCodeElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlPreElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlBlockquoteElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
|
||||
export type HtmlAProperties = HtmlCommonProperties & {
|
||||
href?: string;
|
||||
target?: string;
|
||||
rel?: string;
|
||||
};
|
||||
|
||||
export const HtmlAElement = createRemoteElement<
|
||||
HtmlAProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: {
|
||||
...HTML_COMMON_PROPERTIES_CONFIG,
|
||||
href: { type: String },
|
||||
target: { type: String },
|
||||
rel: { type: String },
|
||||
},
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
|
||||
export type HtmlImgProperties = HtmlCommonProperties & {
|
||||
src?: string;
|
||||
alt?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
};
|
||||
|
||||
export const HtmlImgElement = createRemoteElement<
|
||||
HtmlImgProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: {
|
||||
...HTML_COMMON_PROPERTIES_CONFIG,
|
||||
src: { type: String },
|
||||
alt: { type: String },
|
||||
width: { type: Number },
|
||||
height: { type: Number },
|
||||
},
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlUlElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlOlElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlLiElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
|
||||
export type HtmlFormProperties = HtmlCommonProperties & {
|
||||
action?: string;
|
||||
method?: string;
|
||||
};
|
||||
|
||||
export const HtmlFormElement = createRemoteElement<
|
||||
HtmlFormProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: {
|
||||
...HTML_COMMON_PROPERTIES_CONFIG,
|
||||
action: { type: String },
|
||||
method: { type: String },
|
||||
},
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
|
||||
export type HtmlLabelProperties = HtmlCommonProperties & {
|
||||
htmlFor?: string;
|
||||
};
|
||||
|
||||
export const HtmlLabelElement = createRemoteElement<
|
||||
HtmlLabelProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: {
|
||||
...HTML_COMMON_PROPERTIES_CONFIG,
|
||||
htmlFor: { type: String },
|
||||
},
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
|
||||
export type HtmlInputProperties = HtmlCommonProperties & {
|
||||
type?: string;
|
||||
name?: string;
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
checked?: boolean;
|
||||
readOnly?: boolean;
|
||||
};
|
||||
|
||||
export const HtmlInputElement = createRemoteElement<
|
||||
HtmlInputProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: {
|
||||
...HTML_COMMON_PROPERTIES_CONFIG,
|
||||
type: { type: String },
|
||||
name: { type: String },
|
||||
value: { type: String },
|
||||
placeholder: { type: String },
|
||||
disabled: { type: Boolean },
|
||||
checked: { type: Boolean },
|
||||
readOnly: { type: Boolean },
|
||||
},
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
|
||||
export type HtmlTextareaProperties = HtmlCommonProperties & {
|
||||
name?: string;
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
readOnly?: boolean;
|
||||
rows?: number;
|
||||
cols?: number;
|
||||
};
|
||||
|
||||
export const HtmlTextareaElement = createRemoteElement<
|
||||
HtmlTextareaProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: {
|
||||
...HTML_COMMON_PROPERTIES_CONFIG,
|
||||
name: { type: String },
|
||||
value: { type: String },
|
||||
placeholder: { type: String },
|
||||
disabled: { type: Boolean },
|
||||
readOnly: { type: Boolean },
|
||||
rows: { type: Number },
|
||||
cols: { type: Number },
|
||||
},
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
|
||||
export type HtmlSelectProperties = HtmlCommonProperties & {
|
||||
name?: string;
|
||||
value?: string;
|
||||
disabled?: boolean;
|
||||
multiple?: boolean;
|
||||
};
|
||||
|
||||
export const HtmlSelectElement = createRemoteElement<
|
||||
HtmlSelectProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: {
|
||||
...HTML_COMMON_PROPERTIES_CONFIG,
|
||||
name: { type: String },
|
||||
value: { type: String },
|
||||
disabled: { type: Boolean },
|
||||
multiple: { type: Boolean },
|
||||
},
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
|
||||
export type HtmlOptionProperties = HtmlCommonProperties & {
|
||||
value?: string;
|
||||
disabled?: boolean;
|
||||
selected?: boolean;
|
||||
};
|
||||
|
||||
export const HtmlOptionElement = createRemoteElement<
|
||||
HtmlOptionProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: {
|
||||
...HTML_COMMON_PROPERTIES_CONFIG,
|
||||
value: { type: String },
|
||||
disabled: { type: Boolean },
|
||||
selected: { type: Boolean },
|
||||
},
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
|
||||
export type HtmlButtonProperties = HtmlCommonProperties & {
|
||||
type?: string;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export const HtmlButtonElement = createRemoteElement<
|
||||
HtmlButtonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: {
|
||||
...HTML_COMMON_PROPERTIES_CONFIG,
|
||||
type: { type: String },
|
||||
disabled: { type: Boolean },
|
||||
},
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlTableElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlTheadElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlTbodyElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlTfootElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlTrElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
|
||||
export type HtmlThProperties = HtmlCommonProperties & {
|
||||
colSpan?: number;
|
||||
rowSpan?: number;
|
||||
};
|
||||
|
||||
export const HtmlThElement = createRemoteElement<
|
||||
HtmlThProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: {
|
||||
...HTML_COMMON_PROPERTIES_CONFIG,
|
||||
colSpan: { type: Number },
|
||||
rowSpan: { type: Number },
|
||||
},
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
|
||||
export type HtmlTdProperties = HtmlCommonProperties & {
|
||||
colSpan?: number;
|
||||
rowSpan?: number;
|
||||
};
|
||||
|
||||
export const HtmlTdElement = createRemoteElement<
|
||||
HtmlTdProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: {
|
||||
...HTML_COMMON_PROPERTIES_CONFIG,
|
||||
colSpan: { type: Number },
|
||||
rowSpan: { type: Number },
|
||||
},
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlBrElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlHrElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
|
||||
export type TwentyUiButtonProperties = HtmlCommonProperties & {
|
||||
variant?: string;
|
||||
accent?: string;
|
||||
size?: string;
|
||||
disabled?: boolean;
|
||||
fullWidth?: boolean;
|
||||
};
|
||||
|
||||
export const TwentyUiButtonElement = createRemoteElement<
|
||||
TwentyUiButtonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: {
|
||||
...HTML_COMMON_PROPERTIES_CONFIG,
|
||||
variant: { type: String },
|
||||
accent: { type: String },
|
||||
size: { type: String },
|
||||
disabled: { type: Boolean },
|
||||
fullWidth: { type: Boolean },
|
||||
},
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
customElements.define('html-div', HtmlDivElement);
|
||||
customElements.define('html-span', HtmlSpanElement);
|
||||
customElements.define('html-section', HtmlSectionElement);
|
||||
customElements.define('html-article', HtmlArticleElement);
|
||||
customElements.define('html-header', HtmlHeaderElement);
|
||||
customElements.define('html-footer', HtmlFooterElement);
|
||||
customElements.define('html-main', HtmlMainElement);
|
||||
customElements.define('html-nav', HtmlNavElement);
|
||||
customElements.define('html-aside', HtmlAsideElement);
|
||||
customElements.define('html-p', HtmlPElement);
|
||||
customElements.define('html-h1', HtmlH1Element);
|
||||
customElements.define('html-h2', HtmlH2Element);
|
||||
customElements.define('html-h3', HtmlH3Element);
|
||||
customElements.define('html-h4', HtmlH4Element);
|
||||
customElements.define('html-h5', HtmlH5Element);
|
||||
customElements.define('html-h6', HtmlH6Element);
|
||||
customElements.define('html-strong', HtmlStrongElement);
|
||||
customElements.define('html-em', HtmlEmElement);
|
||||
customElements.define('html-small', HtmlSmallElement);
|
||||
customElements.define('html-code', HtmlCodeElement);
|
||||
customElements.define('html-pre', HtmlPreElement);
|
||||
customElements.define('html-blockquote', HtmlBlockquoteElement);
|
||||
customElements.define('html-a', HtmlAElement);
|
||||
customElements.define('html-img', HtmlImgElement);
|
||||
customElements.define('html-ul', HtmlUlElement);
|
||||
customElements.define('html-ol', HtmlOlElement);
|
||||
customElements.define('html-li', HtmlLiElement);
|
||||
customElements.define('html-form', HtmlFormElement);
|
||||
customElements.define('html-label', HtmlLabelElement);
|
||||
customElements.define('html-input', HtmlInputElement);
|
||||
customElements.define('html-textarea', HtmlTextareaElement);
|
||||
customElements.define('html-select', HtmlSelectElement);
|
||||
customElements.define('html-option', HtmlOptionElement);
|
||||
customElements.define('html-button', HtmlButtonElement);
|
||||
customElements.define('html-table', HtmlTableElement);
|
||||
customElements.define('html-thead', HtmlTheadElement);
|
||||
customElements.define('html-tbody', HtmlTbodyElement);
|
||||
customElements.define('html-tfoot', HtmlTfootElement);
|
||||
customElements.define('html-tr', HtmlTrElement);
|
||||
customElements.define('html-th', HtmlThElement);
|
||||
customElements.define('html-td', HtmlTdElement);
|
||||
customElements.define('html-br', HtmlBrElement);
|
||||
customElements.define('html-hr', HtmlHrElement);
|
||||
customElements.define('twenty-ui-button', TwentyUiButtonElement);
|
||||
customElements.define('remote-root', RemoteRootElement);
|
||||
customElements.define('remote-fragment', RemoteFragmentElement);
|
||||
|
||||
export { RemoteRootElement, RemoteFragmentElement };
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'html-div': InstanceType<typeof HtmlDivElement>;
|
||||
'html-span': InstanceType<typeof HtmlSpanElement>;
|
||||
'html-section': InstanceType<typeof HtmlSectionElement>;
|
||||
'html-article': InstanceType<typeof HtmlArticleElement>;
|
||||
'html-header': InstanceType<typeof HtmlHeaderElement>;
|
||||
'html-footer': InstanceType<typeof HtmlFooterElement>;
|
||||
'html-main': InstanceType<typeof HtmlMainElement>;
|
||||
'html-nav': InstanceType<typeof HtmlNavElement>;
|
||||
'html-aside': InstanceType<typeof HtmlAsideElement>;
|
||||
'html-p': InstanceType<typeof HtmlPElement>;
|
||||
'html-h1': InstanceType<typeof HtmlH1Element>;
|
||||
'html-h2': InstanceType<typeof HtmlH2Element>;
|
||||
'html-h3': InstanceType<typeof HtmlH3Element>;
|
||||
'html-h4': InstanceType<typeof HtmlH4Element>;
|
||||
'html-h5': InstanceType<typeof HtmlH5Element>;
|
||||
'html-h6': InstanceType<typeof HtmlH6Element>;
|
||||
'html-strong': InstanceType<typeof HtmlStrongElement>;
|
||||
'html-em': InstanceType<typeof HtmlEmElement>;
|
||||
'html-small': InstanceType<typeof HtmlSmallElement>;
|
||||
'html-code': InstanceType<typeof HtmlCodeElement>;
|
||||
'html-pre': InstanceType<typeof HtmlPreElement>;
|
||||
'html-blockquote': InstanceType<typeof HtmlBlockquoteElement>;
|
||||
'html-a': InstanceType<typeof HtmlAElement>;
|
||||
'html-img': InstanceType<typeof HtmlImgElement>;
|
||||
'html-ul': InstanceType<typeof HtmlUlElement>;
|
||||
'html-ol': InstanceType<typeof HtmlOlElement>;
|
||||
'html-li': InstanceType<typeof HtmlLiElement>;
|
||||
'html-form': InstanceType<typeof HtmlFormElement>;
|
||||
'html-label': InstanceType<typeof HtmlLabelElement>;
|
||||
'html-input': InstanceType<typeof HtmlInputElement>;
|
||||
'html-textarea': InstanceType<typeof HtmlTextareaElement>;
|
||||
'html-select': InstanceType<typeof HtmlSelectElement>;
|
||||
'html-option': InstanceType<typeof HtmlOptionElement>;
|
||||
'html-button': InstanceType<typeof HtmlButtonElement>;
|
||||
'html-table': InstanceType<typeof HtmlTableElement>;
|
||||
'html-thead': InstanceType<typeof HtmlTheadElement>;
|
||||
'html-tbody': InstanceType<typeof HtmlTbodyElement>;
|
||||
'html-tfoot': InstanceType<typeof HtmlTfootElement>;
|
||||
'html-tr': InstanceType<typeof HtmlTrElement>;
|
||||
'html-th': InstanceType<typeof HtmlThElement>;
|
||||
'html-td': InstanceType<typeof HtmlTdElement>;
|
||||
'html-br': InstanceType<typeof HtmlBrElement>;
|
||||
'html-hr': InstanceType<typeof HtmlHrElement>;
|
||||
'twenty-ui-button': InstanceType<typeof TwentyUiButtonElement>;
|
||||
'remote-root': InstanceType<typeof RemoteRootElement>;
|
||||
'remote-fragment': InstanceType<typeof RemoteFragmentElement>;
|
||||
}
|
||||
}
|
||||
+11
-1
@@ -12,14 +12,18 @@ import {
|
||||
import React from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { jsx, jsxs } from 'react/jsx-runtime';
|
||||
import { type FrontComponentExecutionContext } from '../../types/FrontComponentExecutionContext';
|
||||
import { type HostToWorkerRenderContext } from '../../types/HostToWorkerRenderContext';
|
||||
import { type WorkerExports } from '../../types/WorkerExports';
|
||||
import { frontComponentExecutionContextStore } from '../context/FrontComponentExecutionContextStore';
|
||||
import * as RemoteComponents from '../generated/remote-components';
|
||||
|
||||
(globalThis as Record<string, unknown>).React = React;
|
||||
(globalThis as Record<string, unknown>).RemoteComponents = RemoteComponents;
|
||||
(globalThis as Record<string, unknown>).jsx = jsx;
|
||||
(globalThis as Record<string, unknown>).jsxs = jsxs;
|
||||
(globalThis as Record<string, unknown>).frontComponentExecutionContextStore =
|
||||
frontComponentExecutionContextStore;
|
||||
|
||||
const render: WorkerExports['render'] = async (
|
||||
connection: RemoteConnection,
|
||||
@@ -37,4 +41,10 @@ const render: WorkerExports['render'] = async (
|
||||
reactRoot.render(componentModule.default);
|
||||
};
|
||||
|
||||
ThreadWebWorker.self.export({ render });
|
||||
const updateContext: WorkerExports['updateContext'] = async (
|
||||
context: FrontComponentExecutionContext,
|
||||
) => {
|
||||
frontComponentExecutionContextStore.setContext(context);
|
||||
};
|
||||
|
||||
ThreadWebWorker.self.export({ render, updateContext });
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
export type FrontComponentExecutionContext = {
|
||||
userId: string | null;
|
||||
};
|
||||
+2
@@ -1,4 +1,5 @@
|
||||
import { type RemoteConnection } from '@remote-dom/core/elements';
|
||||
import { type FrontComponentExecutionContext } from './FrontComponentExecutionContext';
|
||||
import { type HostToWorkerRenderContext } from './HostToWorkerRenderContext';
|
||||
|
||||
export type WorkerExports = {
|
||||
@@ -6,4 +7,5 @@ export type WorkerExports = {
|
||||
connection: RemoteConnection,
|
||||
context: HostToWorkerRenderContext,
|
||||
) => Promise<void>;
|
||||
updateContext: (context: FrontComponentExecutionContext) => Promise<void>;
|
||||
};
|
||||
@@ -1,10 +1 @@
|
||||
/*
|
||||
* _____ _
|
||||
*|_ _|_ _____ _ __ | |_ _ _
|
||||
* | | \ \ /\ / / _ \ '_ \| __| | | | Auto-generated file
|
||||
* | | \ V V / __/ | | | |_| |_| | Any edits to this will be overridden
|
||||
* |_| \_/\_/ \___|_| |_|\__|\__, |
|
||||
* |___/
|
||||
*/
|
||||
|
||||
export * from './sdk';
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { type ApplicationConfig } from '@/sdk/application/application-config';
|
||||
import { type FrontComponentConfig } from '@/sdk/front-component-config';
|
||||
import { type LogicFunctionConfig } from '@/sdk/logic-functions/logic-function-config';
|
||||
import {
|
||||
type FieldManifest,
|
||||
type ObjectManifest,
|
||||
type RoleManifest,
|
||||
} from 'twenty-shared/application';
|
||||
import { type FrontComponentConfig } from '@/sdk/front-components/front-component-config';
|
||||
import { type LogicFunctionConfig } from '@/sdk/logic-functions/logic-function-config';
|
||||
import { type ApplicationConfig } from '@/sdk/application/application-config';
|
||||
|
||||
export type ValidationResult<T> = {
|
||||
success: boolean;
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import { type FrontComponentConfig } from '@/sdk/front-components/front-component-config';
|
||||
import type { DefineEntity } from '@/sdk/common/types/define-entity.type';
|
||||
import { createValidationResult } from '@/sdk';
|
||||
import type { DefineEntity } from '@/sdk/common/types/define-entity.type';
|
||||
import { type FrontComponentConfig } from '@/sdk/front-component-config';
|
||||
|
||||
export const defineFrontComponent: DefineEntity<FrontComponentConfig> = (
|
||||
config,
|
||||
@@ -1,12 +1,3 @@
|
||||
/*
|
||||
* _____ _
|
||||
*|_ _|_ _____ _ __ | |_ _ _
|
||||
* | | \ \ /\ / / _ \ '_ \| __| | | | Auto-generated file
|
||||
* | | \ V V / __/ | | | |_| |_| | Any edits to this will be overridden
|
||||
* |_| \_/\_/ \___|_| |_|\__|\__, |
|
||||
* |___/
|
||||
*/
|
||||
|
||||
export type { ApplicationConfig } from './application/application-config';
|
||||
export { defineApplication } from './application/define-application';
|
||||
export type {
|
||||
@@ -16,6 +7,7 @@ export type {
|
||||
} from './common/types/define-entity.type';
|
||||
export type { SyncableEntityOptions } from './common/types/syncable-entity-options.type';
|
||||
export { createValidationResult } from './common/utils/create-validation-result';
|
||||
export { defineFrontComponent } from './define-front-component';
|
||||
export type {
|
||||
ActorField,
|
||||
AddressField,
|
||||
@@ -31,11 +23,10 @@ export { FieldType } from './fields/field-type';
|
||||
export { OnDeleteAction } from './fields/on-delete-action';
|
||||
export { RelationType } from './fields/relation-type';
|
||||
export { validateFields } from './fields/validate-fields';
|
||||
export { defineFrontComponent } from './front-components/define-front-component';
|
||||
export type {
|
||||
FrontComponentType,
|
||||
FrontComponentConfig,
|
||||
} from './front-components/front-component-config';
|
||||
} from './front-component-config';
|
||||
export { defineLogicFunction } from './logic-functions/define-logic-function';
|
||||
export type {
|
||||
LogicFunctionHandler,
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
/*
|
||||
* _____ _
|
||||
*|_ _|_ _____ _ __ | |_ _ _
|
||||
* | | \ \ /\ / / _ \ '_ \| __| | | | Auto-generated file
|
||||
* | | \ V V / __/ | | | |_| |_| | Any edits to this will be overridden
|
||||
* |_| \_/\_/ \___|_| |_|\__|\__, |
|
||||
* |___/
|
||||
*/
|
||||
|
||||
export * from 'twenty-ui';
|
||||
export * from 'twenty-ui/accessibility';
|
||||
export * from 'twenty-ui/components';
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
"**/__mocks__/**/*",
|
||||
"**/__tests__/**/*",
|
||||
"vite.config.ts",
|
||||
"scripts/**/*.ts",
|
||||
"jest.config.mjs"
|
||||
],
|
||||
"exclude": [
|
||||
|
||||
@@ -5,11 +5,13 @@ import dts from 'vite-plugin-dts';
|
||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||
import packageJson from './package.json';
|
||||
|
||||
const moduleEntries = Object.keys((packageJson as any).exports || {})
|
||||
.filter((key) => key !== '.' && !key.startsWith('./src/'))
|
||||
.map((module) => `src/${module.replace(/^\.\//, '')}/index.ts`);
|
||||
|
||||
const entries = ['src/index.ts', 'src/cli/cli.ts', ...moduleEntries];
|
||||
const entries = [
|
||||
'src/index.ts',
|
||||
'src/cli/cli.ts',
|
||||
'src/ui/index.ts',
|
||||
'src/front-component/renderer/index.ts',
|
||||
'src/front-component/api/index.ts',
|
||||
];
|
||||
|
||||
export const PACKAGES_TO_VENDOR = ['twenty-ui', 'twenty-shared'];
|
||||
|
||||
@@ -20,18 +22,15 @@ const entryFileNames = (chunk: any, extension: 'cjs' | 'mjs') => {
|
||||
);
|
||||
}
|
||||
|
||||
const splitFaceModuleId = chunk.facadeModuleId?.split('/');
|
||||
if (splitFaceModuleId === undefined) {
|
||||
throw new Error(
|
||||
`Should never occurs splitFaceModuleId is undefined ${chunk.facadeModuleId}`,
|
||||
);
|
||||
}
|
||||
|
||||
const moduleDirectory = splitFaceModuleId[splitFaceModuleId?.length - 2];
|
||||
if (moduleDirectory === 'src') {
|
||||
// Find which entry this chunk corresponds to
|
||||
const entry = entries.find((e) => chunk.facadeModuleId?.endsWith(e));
|
||||
if (!entry || entry === 'src/index.ts' || entry === 'src/cli/cli.ts') {
|
||||
return `${chunk.name}.${extension}`;
|
||||
}
|
||||
return `${moduleDirectory}/index.${extension}`;
|
||||
|
||||
// Remove 'src/' prefix and '/index.ts' suffix to get the module path
|
||||
const modulePath = entry.replace('src/', '').replace('/index.ts', '');
|
||||
return `${modulePath}/index.${extension}`;
|
||||
};
|
||||
|
||||
const copyTwentyPackagesInVendor = (packages: string[]) => {
|
||||
|
||||
Reference in New Issue
Block a user