Improve building of twenty-sdk (#17913)
## Split twenty-sdk build into separate Node and browser targets The SDK was bundling Node.js code (CLI, SDK API) and browser code (UI components, front-component renderer) through a single Vite config. This caused incorrect externalization — Node builtins leaked into browser bundles and browser-specific chunking logic applied to CLI output. This PR splits the build into `vite.config.node.ts` and `vite.config.browser.ts` so each target gets the right externals and output format. Also includes a few housekeeping renames: - `front-component` export path → `front-component-renderer` (matches what it actually is) - `front-component-common` merged into `front-component-api` (was a needless extra module)
This commit is contained in:
+1
-1
@@ -4,7 +4,7 @@ import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useCallback } from 'react';
|
||||
import { FrontComponentRenderer as SharedFrontComponentRenderer } from 'twenty-sdk/front-component';
|
||||
import { FrontComponentRenderer as SharedFrontComponentRenderer } from 'twenty-sdk/front-component-renderer';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
import { useFindOneFrontComponentQuery } from '~/generated-metadata/graphql';
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { useRecoilValue } from 'recoil';
|
||||
import {
|
||||
type FrontComponentExecutionContext,
|
||||
type FrontComponentHostCommunicationApi,
|
||||
} from 'twenty-sdk/front-component';
|
||||
} from 'twenty-sdk/front-component-renderer';
|
||||
import { type AppPath } from 'twenty-shared/types';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
node_modules
|
||||
.twenty
|
||||
storybook-static
|
||||
src/front-component/__stories__/built
|
||||
src/front-component-renderer/__stories__/example-sources-built
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import type { StorybookConfig } from '@storybook/react-vite';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||
|
||||
const dirname =
|
||||
typeof __dirname !== 'undefined'
|
||||
? __dirname
|
||||
: path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
const sdkRoot = path.resolve(dirname, '..');
|
||||
|
||||
const config: StorybookConfig = {
|
||||
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'],
|
||||
|
||||
@@ -16,7 +19,7 @@ const config: StorybookConfig = {
|
||||
|
||||
staticDirs: [
|
||||
{
|
||||
from: '../src/front-component/__stories__/built',
|
||||
from: '../src/front-component-renderer/__stories__/example-sources-built',
|
||||
to: '/built',
|
||||
},
|
||||
],
|
||||
@@ -31,11 +34,21 @@ const config: StorybookConfig = {
|
||||
'@': path.resolve(dirname, '../src'),
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
...(viteConfig.plugins ?? []),
|
||||
tsconfigPaths({ root: sdkRoot }),
|
||||
],
|
||||
optimizeDeps: {
|
||||
...viteConfig.optimizeDeps,
|
||||
include: [
|
||||
...(viteConfig.optimizeDeps?.include ?? []),
|
||||
'transliteration',
|
||||
'@remote-dom/core/polyfill',
|
||||
'@remote-dom/react/polyfill',
|
||||
'@remote-dom/core/elements',
|
||||
'@remote-dom/react',
|
||||
'react-dom/client',
|
||||
'react/jsx-runtime',
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"version": "0.5.2",
|
||||
"main": "dist/index.cjs",
|
||||
"module": "dist/index.mjs",
|
||||
"types": "dist/index.d.ts",
|
||||
"types": "dist/sdk/index.d.ts",
|
||||
"bin": {
|
||||
"twenty": "dist/cli.cjs"
|
||||
},
|
||||
@@ -13,7 +13,7 @@
|
||||
"package.json"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "npx rimraf dist && npx vite build",
|
||||
"build": "npx rimraf dist && npx vite build -c vite.config.node.ts && npx vite build -c vite.config.browser.ts",
|
||||
"prepublishOnly": "tsx ../twenty-utils/pack-scripts/pre-publish-only.ts",
|
||||
"postpublish": "tsx ../twenty-utils/pack-scripts/post-publish.ts"
|
||||
},
|
||||
@@ -27,7 +27,7 @@
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"types": "./dist/sdk/index.d.ts",
|
||||
"import": "./dist/index.mjs",
|
||||
"require": "./dist/index.cjs"
|
||||
},
|
||||
@@ -36,14 +36,16 @@
|
||||
"import": "./dist/ui/index.mjs",
|
||||
"require": "./dist/ui/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-renderer": {
|
||||
"types": "./dist/front-component-renderer/index.d.ts",
|
||||
"import": "./dist/front-component-renderer/index.mjs",
|
||||
"require": "./dist/front-component-renderer/index.cjs"
|
||||
}
|
||||
},
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@chakra-ui/react": "^3.33.0",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@genql/cli": "^3.0.3",
|
||||
"@quilted/threads": "^4.0.1",
|
||||
"@remote-dom/core": "^1.10.1",
|
||||
@@ -102,8 +104,8 @@
|
||||
"ui": [
|
||||
"dist/ui/index.d.ts"
|
||||
],
|
||||
"front-component": [
|
||||
"dist/front-component/index.d.ts"
|
||||
"front-component-renderer": [
|
||||
"dist/front-component-renderer/index.d.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,18 +3,28 @@
|
||||
"$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": {
|
||||
"executor": "nx:run-commands",
|
||||
"cache": true,
|
||||
"inputs": ["production", "^production"],
|
||||
"dependsOn": ["^build", "generate-remote-dom-elements"],
|
||||
"outputs": ["{projectRoot}/dist"],
|
||||
"inputs": [
|
||||
"production",
|
||||
"^production"
|
||||
],
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
],
|
||||
"outputs": [
|
||||
"{projectRoot}/dist"
|
||||
],
|
||||
"options": {
|
||||
"cwd": "{projectRoot}",
|
||||
"commands": [
|
||||
"npx rimraf dist && npx vite build",
|
||||
"npx rimraf dist && npx vite build -c vite.config.node.ts && npx vite build -c vite.config.browser.ts",
|
||||
"tsgo -p tsconfig.lib.json --declaration --emitDeclarationOnly --noEmit false --outDir dist --rootDir src && npx tsc-alias -p tsconfig.lib.json --outDir dist"
|
||||
],
|
||||
"parallel": false
|
||||
@@ -22,15 +32,19 @@
|
||||
},
|
||||
"dev": {
|
||||
"executor": "nx:run-commands",
|
||||
"dependsOn": ["^build"],
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
],
|
||||
"options": {
|
||||
"cwd": "packages/twenty-sdk",
|
||||
"command": "npx rimraf dist && npx vite build --watch"
|
||||
"command": "npx rimraf dist && npx vite build -c vite.config.node.ts && npx vite build -c vite.config.browser.ts && tsgo -p tsconfig.lib.json --declaration --emitDeclarationOnly --noEmit false --outDir dist --rootDir src && npx tsc-alias -p tsconfig.lib.json --outDir dist && npx vite build -c vite.config.node.ts --watch & npx vite build -c vite.config.browser.ts --watch"
|
||||
}
|
||||
},
|
||||
"start": {
|
||||
"executor": "nx:run-commands",
|
||||
"dependsOn": ["build"],
|
||||
"dependsOn": [
|
||||
"build"
|
||||
],
|
||||
"options": {
|
||||
"cwd": "packages/twenty-sdk",
|
||||
"command": "node dist/cli.cjs"
|
||||
@@ -39,12 +53,16 @@
|
||||
"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": {}
|
||||
@@ -52,7 +70,9 @@
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nx/vitest:test",
|
||||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
||||
"outputs": [
|
||||
"{workspaceRoot}/coverage/{projectRoot}"
|
||||
],
|
||||
"options": {
|
||||
"config": "{projectRoot}/vitest.config.ts"
|
||||
},
|
||||
@@ -99,16 +119,18 @@
|
||||
"generate-remote-dom-elements": {
|
||||
"executor": "nx:run-commands",
|
||||
"cache": true,
|
||||
"dependsOn": ["^build"],
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
],
|
||||
"inputs": [
|
||||
"{projectRoot}/scripts/remote-dom/**/*",
|
||||
"{projectRoot}/src/sdk/front-component-common/**/*",
|
||||
"{projectRoot}/src/sdk/front-component-api/**/*",
|
||||
"{workspaceRoot}/packages/twenty-ui/src/**/index.ts",
|
||||
"{workspaceRoot}/packages/twenty-ui/src/**/*.tsx"
|
||||
],
|
||||
"outputs": [
|
||||
"{projectRoot}/src/front-component/host/generated/*",
|
||||
"{projectRoot}/src/front-component/remote/generated/*"
|
||||
"{projectRoot}/src/front-component-renderer/host/generated/*",
|
||||
"{projectRoot}/src/front-component-renderer/remote/generated/*"
|
||||
],
|
||||
"options": {
|
||||
"cwd": "packages/twenty-sdk",
|
||||
@@ -123,29 +145,33 @@
|
||||
"storybook:prebuild": {
|
||||
"executor": "nx:run-commands",
|
||||
"cache": true,
|
||||
"dependsOn": ["generate-remote-dom-elements"],
|
||||
"inputs": [
|
||||
"{projectRoot}/src/front-component/__stories__/mocks/**/*",
|
||||
"{projectRoot}/src/front-component/__stories__/utils/**/*",
|
||||
"{projectRoot}/src/cli/utilities/build/common/front-component-build/**/*",
|
||||
"{projectRoot}/src/front-component-constants/**/*",
|
||||
"{projectRoot}/src/sdk/**/*"
|
||||
"dependsOn": [
|
||||
"generate-remote-dom-elements"
|
||||
],
|
||||
"inputs": [
|
||||
"{projectRoot}/scripts/front-component-stories/**/*",
|
||||
"{projectRoot}/src/front-component-renderer/__stories__/example-sources/*",
|
||||
"{projectRoot}/src/cli/utilities/build/**/*"
|
||||
],
|
||||
"outputs": [
|
||||
"{projectRoot}/src/front-component-renderer/__stories__/example-sources-built/*"
|
||||
],
|
||||
"outputs": ["{projectRoot}/src/front-component/__stories__/built/*"],
|
||||
"options": {
|
||||
"command": "tsx {projectRoot}/src/front-component/__stories__/utils/buildMockComponents.ts"
|
||||
"command": "tsx {projectRoot}/scripts/front-component-stories/build-source-examples.ts"
|
||||
}
|
||||
},
|
||||
"storybook:build": {
|
||||
"dependsOn": ["storybook:prebuild"],
|
||||
"dependsOn": [
|
||||
"storybook:prebuild"
|
||||
],
|
||||
"configurations": {
|
||||
"test": {}
|
||||
}
|
||||
},
|
||||
"storybook:serve:dev": {
|
||||
"dependsOn": ["storybook:prebuild"],
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"port": 6008
|
||||
"command": "echo 'storybook:serve:dev is disabled for twenty-sdk, use storybook:serve:static instead'"
|
||||
}
|
||||
},
|
||||
"storybook:serve:static": {
|
||||
@@ -158,13 +184,17 @@
|
||||
}
|
||||
},
|
||||
"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}"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import * as esbuild from 'esbuild';
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
import { createFrontComponentBuildOptions } from './utils/create-front-component-build-options';
|
||||
|
||||
const dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const exampleSourcesDir = path.resolve(dirname, '../../src/front-component-renderer/__stories__/example-sources');
|
||||
const exampleSourcesBuiltDir = path.resolve(dirname, '../../src/front-component-renderer/__stories__/example-sources-built');
|
||||
const sdkRoot = path.resolve(dirname, '../../../..');
|
||||
|
||||
const STORY_COMPONENTS = [
|
||||
'static.front-component',
|
||||
'interactive.front-component',
|
||||
'lifecycle.front-component',
|
||||
'chakra-example.front-component',
|
||||
'tailwind-example.front-component',
|
||||
];
|
||||
|
||||
export const buildSourceExamples = async (): Promise<void> => {
|
||||
fs.mkdirSync(exampleSourcesBuiltDir, { recursive: true });
|
||||
|
||||
const entryPoints: Record<string, string> = {};
|
||||
|
||||
for (const name of STORY_COMPONENTS) {
|
||||
const filePath = path.join(exampleSourcesDir, `${name}.tsx`);
|
||||
if (!fs.existsSync(filePath)) {
|
||||
throw new Error(
|
||||
`Story component source file not found: ${filePath}\n` +
|
||||
`Ensure the file exists in ${exampleSourcesDir} and the name in STORY_COMPONENTS is correct.`,
|
||||
);
|
||||
}
|
||||
entryPoints[name] = filePath;
|
||||
}
|
||||
|
||||
const buildOptions = createFrontComponentBuildOptions({
|
||||
entryPoints,
|
||||
outdir: exampleSourcesBuiltDir,
|
||||
tsconfigPath: path.join(dirname, '../../tsconfig.json'),
|
||||
});
|
||||
|
||||
await esbuild.build(buildOptions);
|
||||
|
||||
console.log(
|
||||
`Built ${STORY_COMPONENTS.length} story components to ${exampleSourcesBuiltDir}`,
|
||||
);
|
||||
};
|
||||
|
||||
buildSourceExamples().catch((error) => {
|
||||
console.error('Failed to build mock components:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import type * as esbuild from 'esbuild';
|
||||
|
||||
import { FRONT_COMPONENT_EXTERNAL_MODULES } from '../constants/front-component-external-modules';
|
||||
import { getFrontComponentBuildPlugins } from './get-front-component-build-plugins';
|
||||
import { FRONT_COMPONENT_EXTERNAL_MODULES } from '../../../src/cli/utilities/build/common/front-component-build/constants/front-component-external-modules';
|
||||
import { getFrontComponentBuildPlugins } from '../../../src/cli/utilities/build/common/front-component-build/utils/get-front-component-build-plugins';
|
||||
|
||||
export type FrontComponentBuildOptions = {
|
||||
entryPoints: esbuild.BuildOptions['entryPoints'];
|
||||
@@ -4,10 +4,10 @@ import * as path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { IndentationText, Project, QuoteKind } from 'ts-morph';
|
||||
|
||||
import { ALLOWED_HTML_ELEMENTS } from '../../src/sdk/front-component-common/AllowedHtmlElements';
|
||||
import { COMMON_HTML_EVENTS } from '../../src/sdk/front-component-common/CommonHtmlEvents';
|
||||
import { EVENT_TO_REACT } from '../../src/sdk/front-component-common/EventToReact';
|
||||
import { HTML_COMMON_PROPERTIES } from '../../src/sdk/front-component-common/HtmlCommonProperties';
|
||||
import { ALLOWED_HTML_ELEMENTS } from '../../src/sdk/front-component-api/constants/AllowedHtmlElements';
|
||||
import { COMMON_HTML_EVENTS } from '../../src/sdk/front-component-api/constants/CommonHtmlEvents';
|
||||
import { EVENT_TO_REACT } from '../../src/sdk/front-component-api/constants/EventToReact';
|
||||
import { HTML_COMMON_PROPERTIES } from '../../src/sdk/front-component-api/constants/HtmlCommonProperties';
|
||||
|
||||
import {
|
||||
type ComponentSchema,
|
||||
@@ -39,7 +39,7 @@ const parseVerboseFlag = (): boolean => {
|
||||
|
||||
const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
|
||||
const PACKAGE_PATH = path.resolve(SCRIPT_DIR, '../..');
|
||||
const FRONT_COMPONENT_PATH = path.join(PACKAGE_PATH, 'src/front-component');
|
||||
const FRONT_COMPONENT_PATH = path.join(PACKAGE_PATH, 'src/front-component-renderer');
|
||||
const HOST_GENERATED_DIR = path.join(FRONT_COMPONENT_PATH, 'host/generated');
|
||||
const REMOTE_GENERATED_DIR = path.join(
|
||||
FRONT_COMPONENT_PATH,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Project, SourceFile } from 'ts-morph';
|
||||
|
||||
import { EVENT_TO_REACT } from '@/sdk/front-component-common/EventToReact';
|
||||
import { EVENT_TO_REACT } from '@/sdk/front-component-api/constants/EventToReact';
|
||||
import { isDefined, isNonEmptyArray } from 'twenty-shared/utils';
|
||||
import { CUSTOM_ELEMENT_NAMES } from './constants';
|
||||
import { type ComponentSchema } from './schemas';
|
||||
@@ -275,7 +275,7 @@ export const generateHostRegistry = (
|
||||
});
|
||||
|
||||
sourceFile.addImportDeclaration({
|
||||
moduleSpecifier: '../../../sdk/front-component-common/SerializedEventData',
|
||||
moduleSpecifier: '../../../sdk/front-component-api/constants/SerializedEventData',
|
||||
namedImports: [{ name: 'SerializedEventData', isTypeOnly: true }],
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Project, SourceFile } from 'ts-morph';
|
||||
|
||||
import { EVENT_TO_REACT } from '@/sdk/front-component-common/EventToReact';
|
||||
import { EVENT_TO_REACT } from '@/sdk/front-component-api/constants/EventToReact';
|
||||
import { type ComponentSchema } from './schemas';
|
||||
import { addExportedConst, addFileHeader } from './utils';
|
||||
|
||||
|
||||
@@ -356,7 +356,7 @@ export const generateRemoteElements = (
|
||||
});
|
||||
|
||||
sourceFile.addImportDeclaration({
|
||||
moduleSpecifier: '../../../sdk/front-component-common/SerializedEventData',
|
||||
moduleSpecifier: '../../../sdk/front-component-api/constants/SerializedEventData',
|
||||
namedImports: [{ name: 'SerializedEventData', isTypeOnly: true }],
|
||||
});
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { EVENT_TO_REACT } from '@/sdk/front-component-common/EventToReact';
|
||||
import { EVENT_TO_REACT } from '@/sdk/front-component-api/constants/EventToReact';
|
||||
|
||||
export const REACT_PROP_TO_DOM_EVENT: Record<string, string> =
|
||||
Object.fromEntries(
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import * as path from 'path';
|
||||
import { type ExportSpecifier, Project } from 'ts-morph';
|
||||
import { isDefined, pascalToKebab } from 'twenty-shared/utils';
|
||||
|
||||
import { type PropertySchema } from '@/front-component/types/PropertySchema';
|
||||
import { type PropertySchema } from '@/front-component-renderer/types/PropertySchema';
|
||||
|
||||
import {
|
||||
logCategory,
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { type Type } from 'ts-morph';
|
||||
|
||||
import { type PropertySchema } from '@/front-component/types/PropertySchema';
|
||||
import { type PropertySchema } from '@/front-component-renderer/types/PropertySchema';
|
||||
import { isDefined, isNonEmptyArray } from 'twenty-shared/utils';
|
||||
import { REACT_PROP_TO_DOM_EVENT } from '../constants/ReactPropToDomEvent';
|
||||
import { mapTypeToPropertySchema } from './map-type-to-property-schema';
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { type Type } from 'ts-morph';
|
||||
|
||||
import { type PropertySchema } from '@/front-component/types/PropertySchema';
|
||||
import { type PropertySchema } from '@/front-component-renderer/types/PropertySchema';
|
||||
import { isNonEmptyArray } from 'twenty-shared/utils';
|
||||
|
||||
type PropertyType = PropertySchema['type'];
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type ParsedImportSpecifier } from '../types/ParsedImportSpecifier';
|
||||
|
||||
const ALIASED_IMPORT_PATTERN = /^(\w+)\s+as\s+(\w+)$/;
|
||||
const ALIASED_IMPORT_PATTERN = /^([\w$]+)\s+as\s+([\w$]+)$/;
|
||||
|
||||
export const extractNamesFromImportSpecifier = (
|
||||
importSpecifier: string,
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { HTML_TAG_TO_REMOTE_COMPONENT } from '../../../../../../sdk/front-component-common';
|
||||
import { HTML_TAG_TO_REMOTE_COMPONENT } from '../../../../../../sdk/front-component-api';
|
||||
|
||||
const REMOTE_COMPONENTS_GLOBAL_NAMESPACE = 'RemoteComponents';
|
||||
|
||||
|
||||
+26
-10
@@ -3,7 +3,7 @@ import { expect, fn, userEvent, waitFor, within } from 'storybook/test';
|
||||
|
||||
import { FrontComponentRenderer } from '../host/components/FrontComponentRenderer';
|
||||
|
||||
import { getBuiltComponentPath } from './utils/loadBuiltComponent';
|
||||
import { getBuiltStoryComponentPathForRender } from './utils/getBuiltStoryComponentPathForRender';
|
||||
|
||||
const errorHandler = fn();
|
||||
|
||||
@@ -27,7 +27,7 @@ type Story = StoryObj<typeof FrontComponentRenderer>;
|
||||
|
||||
export const Static: Story = {
|
||||
args: {
|
||||
componentUrl: getBuiltComponentPath('static.front-component'),
|
||||
componentUrl: getBuiltStoryComponentPathForRender('static.front-component'),
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
@@ -35,7 +35,7 @@ export const Static: Story = {
|
||||
const container = await canvas.findByTestId(
|
||||
'static-component',
|
||||
{},
|
||||
{ timeout: 5000 },
|
||||
{ timeout: 30000 },
|
||||
);
|
||||
expect(container).toBeVisible();
|
||||
expect(container).toHaveStyle({
|
||||
@@ -55,12 +55,12 @@ export const Static: Story = {
|
||||
|
||||
export const Interactive: Story = {
|
||||
args: {
|
||||
componentUrl: getBuiltComponentPath('interactive.front-component'),
|
||||
componentUrl: getBuiltStoryComponentPathForRender('interactive.front-component'),
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
await canvas.findByTestId('interactive-component', {}, { timeout: 5000 });
|
||||
await canvas.findByTestId('interactive-component', {}, { timeout: 10000 });
|
||||
|
||||
expect(await canvas.findByText('Count: 0')).toBeVisible();
|
||||
|
||||
@@ -75,12 +75,12 @@ export const Interactive: Story = {
|
||||
|
||||
export const Lifecycle: Story = {
|
||||
args: {
|
||||
componentUrl: getBuiltComponentPath('lifecycle.front-component'),
|
||||
componentUrl: getBuiltStoryComponentPathForRender('lifecycle.front-component'),
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
await canvas.findByTestId('lifecycle-component', {}, { timeout: 5000 });
|
||||
await canvas.findByTestId('lifecycle-component', {}, { timeout: 10000 });
|
||||
|
||||
expect(await canvas.findByText('Mounted')).toBeVisible();
|
||||
|
||||
@@ -89,21 +89,37 @@ export const Lifecycle: Story = {
|
||||
const tickElement = canvas.getByTestId('tick-count');
|
||||
expect(tickElement.textContent).toMatch(/Ticks: [1-9]\d*/);
|
||||
},
|
||||
{ timeout: 5000 },
|
||||
{ timeout: 10000 },
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export const ChakraExample: Story = {
|
||||
args: {
|
||||
componentUrl: getBuiltStoryComponentPathForRender(
|
||||
'chakra-example.front-component',
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
export const TailwindExample: Story = {
|
||||
args: {
|
||||
componentUrl: getBuiltStoryComponentPathForRender(
|
||||
'tailwind-example.front-component',
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
export const ErrorHandling: Story = {
|
||||
args: {
|
||||
componentUrl: '/built/nonexistent.front-component.mjs',
|
||||
componentUrl: getBuiltStoryComponentPathForRender('nonexistent.front-component'),
|
||||
},
|
||||
play: async () => {
|
||||
await waitFor(
|
||||
() => {
|
||||
expect(errorHandler).toHaveBeenCalled();
|
||||
},
|
||||
{ timeout: 5000 },
|
||||
{ timeout: 10000 },
|
||||
);
|
||||
},
|
||||
};
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import { Button, ChakraProvider, defaultSystem } from '@chakra-ui/react';
|
||||
import { defineFrontComponent } from 'twenty-sdk';
|
||||
|
||||
export const ChakraComponent = () => {
|
||||
return (
|
||||
<ChakraProvider value={defaultSystem}>
|
||||
<div style={{ padding: '20px' }}>
|
||||
<Button colorPalette="blue" size="md">
|
||||
Click me
|
||||
</Button>
|
||||
</div>
|
||||
</ChakraProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default defineFrontComponent({
|
||||
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
name: 'chakra-component',
|
||||
description: 'A front component with a Chakra UI button',
|
||||
component: ChakraComponent,
|
||||
});
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
import { defineFrontComponent } from '@/sdk';
|
||||
import { useState } from 'react';
|
||||
|
||||
// Tailwind CSS subset (only the utilities used by this component)
|
||||
// In a real setup, this would be generated by the Tailwind CLI at build time.
|
||||
const TAILWIND_CSS = `
|
||||
.p-5{padding:1.25rem}
|
||||
.mb-4{margin-bottom:1rem}
|
||||
.mb-2{margin-bottom:.5rem}
|
||||
.space-y-3>:not(:first-child){margin-top:.75rem}
|
||||
.rounded-lg{border-radius:.5rem}
|
||||
.rounded-md{border-radius:.375rem}
|
||||
.border{border-width:1px}
|
||||
.border-gray-200{border-color:#e5e7eb}
|
||||
.bg-white{background-color:#fff}
|
||||
.bg-blue-600{background-color:#2563eb}
|
||||
.bg-blue-700{background-color:#1d4ed8}
|
||||
.bg-gray-50{background-color:#f9fafb}
|
||||
.px-4{padding-left:1rem;padding-right:1rem}
|
||||
.py-2{padding-top:.5rem;padding-bottom:.5rem}
|
||||
.text-sm{font-size:.875rem;line-height:1.25rem}
|
||||
.text-lg{font-size:1.125rem;line-height:1.75rem}
|
||||
.text-2xl{font-size:1.5rem;line-height:2rem}
|
||||
.font-semibold{font-weight:600}
|
||||
.font-bold{font-weight:700}
|
||||
.text-gray-500{color:#6b7280}
|
||||
.text-gray-900{color:#111827}
|
||||
.text-blue-600{color:#2563eb}
|
||||
.text-white{color:#fff}
|
||||
.shadow-sm{box-shadow:0 1px 2px 0 rgb(0 0 0/.05)}
|
||||
.cursor-pointer{cursor:pointer}
|
||||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
||||
`;
|
||||
|
||||
const TailwindComponent = () => {
|
||||
const [count, setCount] = useState(0);
|
||||
|
||||
return (
|
||||
<>
|
||||
<style>{TAILWIND_CSS}</style>
|
||||
<div
|
||||
data-testid="tailwind-component"
|
||||
className="p-5 bg-white rounded-lg border border-gray-200 shadow-sm space-y-3"
|
||||
>
|
||||
<h2 className="text-lg font-bold text-gray-900">
|
||||
Tailwind CSS Component
|
||||
</h2>
|
||||
<p className="text-sm text-gray-500">
|
||||
This component uses Tailwind utility classes via className.
|
||||
</p>
|
||||
<div className="bg-gray-50 rounded-md p-5">
|
||||
<span
|
||||
data-testid="tailwind-count"
|
||||
className="text-2xl font-semibold text-blue-600"
|
||||
>
|
||||
Count: {count}
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
data-testid="tailwind-button"
|
||||
className="px-4 py-2 bg-blue-600 text-white font-semibold rounded-md text-sm cursor-pointer"
|
||||
onClick={() => setCount((previous) => previous + 1)}
|
||||
>
|
||||
Increment
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default defineFrontComponent({
|
||||
universalIdentifier: 'test-tailwind-0000-0000-0000-000000000005',
|
||||
name: 'tailwind-component',
|
||||
description: 'A front component using Tailwind CSS utility classes',
|
||||
component: TailwindComponent,
|
||||
});
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// Returns an absolute URL because the worker runs inside a Blob URL
|
||||
// where relative paths cannot be resolved.
|
||||
export const getBuiltStoryComponentPathForRender = (
|
||||
componentName: string,
|
||||
): string => {
|
||||
const origin = typeof window !== 'undefined' ? window.location.origin : '';
|
||||
|
||||
return `${origin}/built/${componentName}.mjs`;
|
||||
};
|
||||
+26
-7
@@ -1,9 +1,9 @@
|
||||
import { FrontComponentErrorEffect } from '@/front-component/remote/components/FrontComponentErrorEffect';
|
||||
import { FrontComponentHostCommunicationApiEffect } from '@/front-component/remote/components/FrontComponentHostCommunicationApiEffect';
|
||||
import { FrontComponentUpdateContextEffect } from '@/front-component/remote/components/FrontComponentUpdateContextEffect';
|
||||
import { type FrontComponentExecutionContext } from '@/front-component/types/FrontComponentExecutionContext';
|
||||
import { type FrontComponentHostCommunicationApi } from '@/front-component/types/FrontComponentHostCommunicationApi';
|
||||
import { type WorkerExports } from '@/front-component/types/WorkerExports';
|
||||
import { FrontComponentErrorEffect } from '@/front-component-renderer/remote/components/FrontComponentErrorEffect';
|
||||
import { FrontComponentHostCommunicationApiEffect } from '@/front-component-renderer/remote/components/FrontComponentHostCommunicationApiEffect';
|
||||
import { FrontComponentUpdateContextEffect } from '@/front-component-renderer/remote/components/FrontComponentUpdateContextEffect';
|
||||
import { type FrontComponentExecutionContext } from '@/front-component-renderer/types/FrontComponentExecutionContext';
|
||||
import { type FrontComponentHostCommunicationApi } from '@/front-component-renderer/types/FrontComponentHostCommunicationApi';
|
||||
import { type WorkerExports } from '@/front-component-renderer/types/WorkerExports';
|
||||
import { type ThreadWebWorker } from '@quilted/threads';
|
||||
import {
|
||||
type RemoteReceiver,
|
||||
@@ -70,7 +70,26 @@ export const FrontComponentRenderer = ({
|
||||
{MemoizedFrontComponentWorkerEffect}
|
||||
|
||||
{isDefined(error) && (
|
||||
<FrontComponentErrorEffect error={error} onError={onError} />
|
||||
<>
|
||||
<FrontComponentErrorEffect error={error} onError={onError} />
|
||||
<div
|
||||
style={{
|
||||
padding: '12px 16px',
|
||||
backgroundColor: '#fef2f2',
|
||||
border: '1px solid #fecaca',
|
||||
borderRadius: '6px',
|
||||
color: '#991b1b',
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '13px',
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordBreak: 'break-word',
|
||||
maxHeight: '200px',
|
||||
overflow: 'auto',
|
||||
}}
|
||||
>
|
||||
<strong>FrontComponent error:</strong> {error.message}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{isDefined(thread) && (
|
||||
+1
-1
@@ -12,7 +12,7 @@ import {
|
||||
RemoteFragmentRenderer,
|
||||
createRemoteComponentRenderer,
|
||||
} from '@remote-dom/react/host';
|
||||
import { type SerializedEventData } from '../../../sdk/front-component-common/SerializedEventData';
|
||||
import { type SerializedEventData } from '../../../sdk/front-component-api/constants/SerializedEventData';
|
||||
import {
|
||||
AnimatedButton,
|
||||
AnimatedLightIconButton,
|
||||
+1
-1
@@ -114,7 +114,7 @@ export type {
|
||||
HtmlThProperties,
|
||||
TwentyUiButtonProperties,
|
||||
} from './remote/generated/remote-elements';
|
||||
export { createRemoteWorker } from './remote/worker/createRemoteWorker';
|
||||
export { createRemoteWorker } from './remote/worker/utils/createRemoteWorker';
|
||||
export type { FrontComponentExecutionContext } from './types/FrontComponentExecutionContext';
|
||||
export type { FrontComponentHostCommunicationApi } from './types/FrontComponentHostCommunicationApi';
|
||||
export type { HostToWorkerRenderContext } from './types/HostToWorkerRenderContext';
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { type FrontComponentHostCommunicationApi } from '@/front-component/types/FrontComponentHostCommunicationApi';
|
||||
import { type WorkerExports } from '@/front-component/types/WorkerExports';
|
||||
import { type FrontComponentHostCommunicationApi } from '@/front-component-renderer/types/FrontComponentHostCommunicationApi';
|
||||
import { type WorkerExports } from '@/front-component-renderer/types/WorkerExports';
|
||||
import { type ThreadWebWorker } from '@quilted/threads';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
import { type FrontComponentExecutionContext } from '@/front-component/types/FrontComponentExecutionContext';
|
||||
import { type FrontComponentHostCommunicationApi } from '@/front-component/types/FrontComponentHostCommunicationApi';
|
||||
import { type WorkerExports } from '@/front-component/types/WorkerExports';
|
||||
import { type FrontComponentExecutionContext } from '@/front-component-renderer/types/FrontComponentExecutionContext';
|
||||
import { type FrontComponentHostCommunicationApi } from '@/front-component-renderer/types/FrontComponentHostCommunicationApi';
|
||||
import { type WorkerExports } from '@/front-component-renderer/types/WorkerExports';
|
||||
import { type ThreadWebWorker } from '@quilted/threads';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
+7
-2
@@ -3,7 +3,7 @@ import { RemoteReceiver } from '@remote-dom/core/receivers';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { type FrontComponentHostCommunicationApi } from '../../types/FrontComponentHostCommunicationApi';
|
||||
import { type WorkerExports } from '../../types/WorkerExports';
|
||||
import { createRemoteWorker } from '../worker/createRemoteWorker';
|
||||
import { createRemoteWorker } from '../worker/utils/createRemoteWorker';
|
||||
|
||||
type FrontComponentWorkerEffectProps = {
|
||||
componentUrl: string;
|
||||
@@ -41,7 +41,12 @@ export const FrontComponentWorkerEffect = ({
|
||||
const worker = createRemoteWorker();
|
||||
|
||||
worker.onerror = (event: ErrorEvent) => {
|
||||
setError(event.error);
|
||||
const workerError =
|
||||
event.error ??
|
||||
new Error(event.message || 'Unknown worker error');
|
||||
|
||||
console.error('[FrontComponentRenderer] Worker error:', workerError);
|
||||
setError(workerError);
|
||||
};
|
||||
|
||||
// Expose host functions to the worker via stable refs to avoid recreating threads
|
||||
+1
-1
@@ -13,7 +13,7 @@ import {
|
||||
RemoteFragmentElement,
|
||||
type RemoteEvent,
|
||||
} from '@remote-dom/core/elements';
|
||||
import { type SerializedEventData } from '../../../sdk/front-component-common/SerializedEventData';
|
||||
import { type SerializedEventData } from '../../../sdk/front-component-api/constants/SerializedEventData';
|
||||
|
||||
export type HtmlCommonProperties = {
|
||||
id?: string;
|
||||
+6
@@ -85,6 +85,12 @@ const render: WorkerExports['render'] = async (
|
||||
|
||||
const reactRoot = createRoot(root);
|
||||
reactRoot.render(componentModule.default);
|
||||
} catch (importError) {
|
||||
console.error(
|
||||
'[FrontComponentWorker] Failed to load or render component:',
|
||||
importError,
|
||||
);
|
||||
throw importError;
|
||||
} finally {
|
||||
URL.revokeObjectURL(importUrl);
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
// @ts-expect-error - Vite worker inline import
|
||||
import RemoteWorker from './remote-worker?worker&inline';
|
||||
import RemoteWorker from '../remote-worker?worker&inline';
|
||||
|
||||
export const createRemoteWorker = (): Worker => {
|
||||
return new RemoteWorker();
|
||||
@@ -1,51 +0,0 @@
|
||||
import * as esbuild from 'esbuild';
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
import { createFrontComponentBuildOptions } from '../../../cli/utilities/build/common/front-component-build/utils/create-front-component-build-options';
|
||||
|
||||
const dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const mocksDir = path.resolve(dirname, '../mocks');
|
||||
const outputDir = path.resolve(dirname, '../built');
|
||||
const sdkRoot = path.resolve(dirname, '../../../..');
|
||||
|
||||
const STORY_COMPONENTS = [
|
||||
'static.front-component',
|
||||
'interactive.front-component',
|
||||
'lifecycle.front-component',
|
||||
];
|
||||
|
||||
export const buildMockComponents = async (): Promise<void> => {
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
|
||||
const entryPoints: Record<string, string> = {};
|
||||
|
||||
for (const name of STORY_COMPONENTS) {
|
||||
const filePath = path.join(mocksDir, `${name}.tsx`);
|
||||
if (!fs.existsSync(filePath)) {
|
||||
throw new Error(
|
||||
`Story component source file not found: ${filePath}\n` +
|
||||
`Ensure the file exists in ${mocksDir} and the name in STORY_COMPONENTS is correct.`,
|
||||
);
|
||||
}
|
||||
entryPoints[name] = filePath;
|
||||
}
|
||||
|
||||
const buildOptions = createFrontComponentBuildOptions({
|
||||
entryPoints,
|
||||
outdir: outputDir,
|
||||
tsconfigPath: path.join(sdkRoot, 'tsconfig.json'),
|
||||
});
|
||||
|
||||
await esbuild.build(buildOptions);
|
||||
|
||||
console.log(
|
||||
`Built ${STORY_COMPONENTS.length} story components to ${outputDir}`,
|
||||
);
|
||||
};
|
||||
|
||||
buildMockComponents().catch((error) => {
|
||||
console.error('Failed to build mock components:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1,3 +0,0 @@
|
||||
export const getBuiltComponentPath = (componentName: string): string => {
|
||||
return `/built/${componentName}.mjs`;
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export * from './sdk';
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { type PropertySchema } from '@/front-component/types/PropertySchema';
|
||||
import { type PropertySchema } from '@/front-component-renderer/types/PropertySchema';
|
||||
|
||||
export const HTML_COMMON_PROPERTIES: Record<string, PropertySchema> = {
|
||||
id: { type: 'string', optional: true },
|
||||
@@ -3,3 +3,11 @@ export { navigate, setNavigate } from './functions/navigate';
|
||||
export { useFrontComponentExecutionContext } from './hooks/useFrontComponentExecutionContext';
|
||||
export { useUserId } from './hooks/useUserId';
|
||||
export type { FrontComponentExecutionContext } from './types/FrontComponentExecutionContext';
|
||||
|
||||
export type { AllowedHtmlElement } from './constants/AllowedHtmlElements';
|
||||
export { ALLOWED_HTML_ELEMENTS } from './constants/AllowedHtmlElements';
|
||||
export { COMMON_HTML_EVENTS } from './constants/CommonHtmlEvents';
|
||||
export { EVENT_TO_REACT } from './constants/EventToReact';
|
||||
export { HTML_COMMON_PROPERTIES } from './constants/HtmlCommonProperties';
|
||||
export { HTML_TAG_TO_REMOTE_COMPONENT } from './constants/HtmlTagToRemoteComponent';
|
||||
export type { SerializedEventData } from './constants/SerializedEventData';
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
* _____ _
|
||||
*|_ _|_ _____ _ __ | |_ _ _
|
||||
* | | \ \ /\ / / _ \ '_ \| __| | | | Auto-generated file
|
||||
* | | \ V V / __/ | | | |_| |_| | Any edits to this will be overridden
|
||||
* |_| \_/\_/ \___|_| |_|\__|\__, |
|
||||
* |___/
|
||||
*/
|
||||
|
||||
export type { AllowedHtmlElement } from './AllowedHtmlElements';
|
||||
export { ALLOWED_HTML_ELEMENTS } from './AllowedHtmlElements';
|
||||
export { COMMON_HTML_EVENTS } from './CommonHtmlEvents';
|
||||
export { EVENT_TO_REACT } from './EventToReact';
|
||||
export { HTML_COMMON_PROPERTIES } from './HtmlCommonProperties';
|
||||
export { HTML_TAG_TO_REMOTE_COMPONENT } from './HtmlTagToRemoteComponent';
|
||||
export type { SerializedEventData } from './SerializedEventData';
|
||||
@@ -57,9 +57,9 @@ export { useUserId } from './front-component-api';
|
||||
export type { FrontComponentExecutionContext } from './front-component-api';
|
||||
|
||||
// Front Component Common exports
|
||||
export type { AllowedHtmlElement } from './front-component-common';
|
||||
export { ALLOWED_HTML_ELEMENTS } from './front-component-common';
|
||||
export { COMMON_HTML_EVENTS } from './front-component-common';
|
||||
export { EVENT_TO_REACT } from './front-component-common';
|
||||
export { HTML_COMMON_PROPERTIES } from './front-component-common';
|
||||
export { HTML_TAG_TO_REMOTE_COMPONENT } from './front-component-common';
|
||||
export type { AllowedHtmlElement } from './front-component-api';
|
||||
export { ALLOWED_HTML_ELEMENTS } from './front-component-api';
|
||||
export { COMMON_HTML_EVENTS } from './front-component-api';
|
||||
export { EVENT_TO_REACT } from './front-component-api';
|
||||
export { HTML_COMMON_PROPERTIES } from './front-component-api';
|
||||
export { HTML_TAG_TO_REMOTE_COMPONENT } from './front-component-api';
|
||||
|
||||
@@ -26,12 +26,14 @@
|
||||
"**/__mocks__/**/*",
|
||||
"**/__tests__/**/*",
|
||||
"vite.config.ts",
|
||||
"vite.config.node.ts",
|
||||
"vite.config.browser.ts",
|
||||
"jest.config.mjs"
|
||||
],
|
||||
"exclude": [
|
||||
"src/front-component/remote/mock/**/*",
|
||||
"src/front-component/host/generated/host-component-registry.ts",
|
||||
"src/front-component/remote/generated/remote-components.ts",
|
||||
"src/front-component/remote/generated/remote-elements.ts"
|
||||
"src/front-component-renderer/remote/mock/**/*",
|
||||
"src/front-component-renderer/host/generated/host-component-registry.ts",
|
||||
"src/front-component-renderer/remote/generated/remote-components.ts",
|
||||
"src/front-component-renderer/remote/generated/remote-elements.ts"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,38 +1,38 @@
|
||||
import path from 'path';
|
||||
import { PackageJson } from 'type-fest';
|
||||
import { defineConfig } from 'vite';
|
||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||
import packageJson from './package.json';
|
||||
import { type PackageJson } from 'type-fest';
|
||||
|
||||
const entries = [
|
||||
'src/index.ts',
|
||||
'src/cli/cli.ts',
|
||||
'src/ui/index.ts',
|
||||
'src/front-component/index.ts',
|
||||
];
|
||||
import packageJson from './package.json';
|
||||
|
||||
|
||||
const entries = ['src/ui/index.ts', 'src/front-component-renderer/index.ts'];
|
||||
|
||||
const entryFileNames = (chunk: any, extension: 'cjs' | 'mjs') => {
|
||||
if (!chunk.isEntry) {
|
||||
throw new Error(
|
||||
`Should never occurs, encountered a non entry chunk ${chunk.facadeModuleId}`,
|
||||
`Should never occur, encountered a non-entry chunk ${chunk.facadeModuleId}`,
|
||||
);
|
||||
}
|
||||
|
||||
// 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') {
|
||||
const entry = entries.find((entryPath) =>
|
||||
chunk.facadeModuleId?.endsWith(entryPath),
|
||||
);
|
||||
|
||||
if (!entry) {
|
||||
return `${chunk.name}.${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}`;
|
||||
};
|
||||
|
||||
export default defineConfig(() => {
|
||||
return {
|
||||
root: __dirname,
|
||||
cacheDir: '../../node_modules/.vite/packages/twenty-sdk',
|
||||
cacheDir: '../../node_modules/.vite/packages/twenty-sdk-browser',
|
||||
resolve: {
|
||||
alias: {
|
||||
'@/': path.resolve(__dirname, 'src') + '/',
|
||||
@@ -61,11 +61,11 @@ export default defineConfig(() => {
|
||||
],
|
||||
},
|
||||
build: {
|
||||
emptyOutDir: false,
|
||||
outDir: 'dist',
|
||||
lib: { entry: entries, name: 'twenty-sdk' },
|
||||
rollupOptions: {
|
||||
onwarn: (warning, warn) => {
|
||||
// Suppress "use client" directive warnings from framer-motion
|
||||
if (
|
||||
warning.code === 'MODULE_LEVEL_DIRECTIVE' &&
|
||||
warning.message.includes('"use client"')
|
||||
@@ -75,26 +75,6 @@ export default defineConfig(() => {
|
||||
warn(warning);
|
||||
},
|
||||
external: (id: string) => {
|
||||
if (/^node:/.test(id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const builtins = [
|
||||
'path',
|
||||
'fs',
|
||||
'fs/promises',
|
||||
'url',
|
||||
'crypto',
|
||||
'stream',
|
||||
'util',
|
||||
'os',
|
||||
'module',
|
||||
];
|
||||
|
||||
if (builtins.includes(id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const deps = Object.entries(
|
||||
(packageJson as PackageJson).dependencies || {},
|
||||
).filter(([_, version]) => !version?.startsWith('workspace:'));
|
||||
@@ -119,16 +99,5 @@ export default defineConfig(() => {
|
||||
},
|
||||
},
|
||||
logLevel: 'warn',
|
||||
optimizeDeps: {
|
||||
include: [
|
||||
'@remote-dom/core/polyfill',
|
||||
'@remote-dom/react/polyfill',
|
||||
'@remote-dom/core/elements',
|
||||
'@remote-dom/react',
|
||||
'@remote-dom/react/host',
|
||||
'react-dom/client',
|
||||
'react/jsx-runtime',
|
||||
],
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,76 @@
|
||||
import path from 'path';
|
||||
import { type PackageJson } from 'type-fest';
|
||||
import { defineConfig } from 'vite';
|
||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||
|
||||
import packageJson from './package.json';
|
||||
|
||||
export default defineConfig(() => {
|
||||
return {
|
||||
root: __dirname,
|
||||
cacheDir: '../../node_modules/.vite/packages/twenty-sdk-node',
|
||||
resolve: {
|
||||
alias: {
|
||||
'@/': path.resolve(__dirname, 'src') + '/',
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
tsconfigPaths({
|
||||
root: __dirname,
|
||||
}),
|
||||
],
|
||||
build: {
|
||||
emptyOutDir: false,
|
||||
outDir: 'dist',
|
||||
lib: {
|
||||
entry: ['src/sdk/index.ts', 'src/cli/cli.ts'],
|
||||
name: 'twenty-sdk',
|
||||
},
|
||||
rollupOptions: {
|
||||
external: (id: string) => {
|
||||
if (/^node:/.test(id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const builtins = [
|
||||
'path',
|
||||
'fs',
|
||||
'fs/promises',
|
||||
'url',
|
||||
'crypto',
|
||||
'stream',
|
||||
'util',
|
||||
'os',
|
||||
'module',
|
||||
];
|
||||
|
||||
if (builtins.includes(id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const deps = Object.entries(
|
||||
(packageJson as PackageJson).dependencies || {},
|
||||
).filter(([_, version]) => !version?.startsWith('workspace:'));
|
||||
|
||||
return deps.some(
|
||||
([dep]) => id === dep || id.startsWith(dep + '/'),
|
||||
);
|
||||
},
|
||||
output: [
|
||||
{
|
||||
format: 'es' as const,
|
||||
entryFileNames: '[name].mjs',
|
||||
},
|
||||
{
|
||||
format: 'cjs' as const,
|
||||
interop: 'auto' as const,
|
||||
esModule: true,
|
||||
exports: 'named' as const,
|
||||
entryFileNames: '[name].cjs',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
logLevel: 'warn' as const,
|
||||
};
|
||||
});
|
||||
@@ -20,7 +20,7 @@ export default defineConfig({
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
extends: './vite.config.ts',
|
||||
extends: './vite.config.browser.ts',
|
||||
plugins: [
|
||||
storybookTest({
|
||||
configDir: path.join(dirname, '.storybook'),
|
||||
@@ -37,6 +37,7 @@ export default defineConfig({
|
||||
},
|
||||
setupFiles: ['./.storybook/vitest.setup.ts'],
|
||||
testTimeout: 5 * MINUTES_IN_MS,
|
||||
retry: 2,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user