[REFACTOR] twenty-shared multi barrel and CJS/ESM build with preconstruct (#11083)

# Introduction

In this PR we've migrated `twenty-shared` from a `vite` app
[libary-mode](https://vite.dev/guide/build#library-mode) to a
[preconstruct](https://preconstruct.tools/) "atomic" application ( in
the future would like to introduce preconstruct to handle of all our
atomic dependencies such as `twenty-emails` `twenty-ui` etc it will be
integrated at the monorepo's root directly, would be to invasive in the
first, starting incremental via `twenty-shared`)

For more information regarding the motivations please refer to nor:
- https://github.com/twentyhq/core-team-issues/issues/587
-
https://github.com/twentyhq/core-team-issues/issues/281#issuecomment-2630949682

close https://github.com/twentyhq/core-team-issues/issues/589
close https://github.com/twentyhq/core-team-issues/issues/590

## How to test
In order to ease the review this PR will ship all the codegen at the
very end, the actual meaning full diff is `+2,411 −114`
In order to migrate existing dependent packages to `twenty-shared` multi
barrel new arch you need to run in local:
```sh
yarn tsx packages/twenty-shared/scripts/migrateFromSingleToMultiBarrelImport.ts && \
npx nx run-many -t lint --fix -p twenty-front twenty-ui twenty-server twenty-emails twenty-shared twenty-zapier
```
Note that `migrateFromSingleToMultiBarrelImport` is idempotent, it's atm
included in the PR but should not be merged. ( such as codegen will be
added before merging this script will be removed )

## Misc
- related opened issue preconstruct
https://github.com/preconstruct/preconstruct/issues/617

## Closed related PR
- https://github.com/twentyhq/twenty/pull/11028
- https://github.com/twentyhq/twenty/pull/10993
- https://github.com/twentyhq/twenty/pull/10960

## Upcoming enhancement: ( in others dedicated PRs )
- 1/ refactor generate barrel to export atomic module instead of `*`
- 2/ generate barrel own package with several files and tests
- 3/ Migration twenty-ui the same way
- 4/ Use `preconstruct` at monorepo global level

## Conclusion
As always any suggestions are welcomed !
This commit is contained in:
Paul Rastoin
2025-03-22 19:16:06 +01:00
committed by GitHub
parent 8a21c19f03
commit 9ad8287dbc
1091 changed files with 3611 additions and 1297 deletions

View File

@ -5,10 +5,10 @@ import { useSearchParams } from 'react-router-dom';
import { useRedirect } from '@/domain-manager/hooks/useRedirect';
import { useLingui } from '@lingui/react/macro';
import { isDefined } from 'twenty-shared';
import { MainButton, UndecoratedLink } from 'twenty-ui';
import { useAuthorizeAppMutation } from '~/generated/graphql';
import { useNavigateApp } from '~/hooks/useNavigateApp';
import { isDefined } from 'twenty-shared/utils';
type App = { id: string; name: string; logo: string };

View File

@ -18,13 +18,13 @@ import { useIsCurrentLocationOnAWorkspace } from '@/domain-manager/hooks/useIsCu
import { useIsCurrentLocationOnDefaultDomain } from '@/domain-manager/hooks/useIsCurrentLocationOnDefaultDomain';
import { DEFAULT_WORKSPACE_NAME } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceName';
import { useMemo } from 'react';
import { isDefined } from 'twenty-shared';
import { AnimatedEaseIn } from 'twenty-ui';
import { useWorkspaceFromInviteHash } from '@/auth/sign-in-up/hooks/useWorkspaceFromInviteHash';
import { useLingui } from '@lingui/react/macro';
import { useSearchParams } from 'react-router-dom';
import { PublicWorkspaceDataOutput } from '~/generated/graphql';
import { isDefined } from 'twenty-shared/utils';
const StandardContent = ({
workspacePublicData,

View File

@ -2,7 +2,8 @@ import { useLabelIdentifierFieldMetadataItem } from '@/object-metadata/hooks/use
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { PageTitle } from '@/ui/utilities/page-title/components/PageTitle';
import { useRecoilValue } from 'recoil';
import { FieldMetadataType, capitalize, isDefined } from 'twenty-shared';
import { FieldMetadataType } from 'twenty-shared/types';
import { capitalize, isDefined } from 'twenty-shared/utils';
export const RecordShowPageTitle = ({
objectNameSingular,

View File

@ -11,7 +11,6 @@ import { billingState } from '@/client-config/states/billingState';
import styled from '@emotion/styled';
import { Trans, useLingui } from '@lingui/react/macro';
import { useRecoilState, useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-shared';
import {
ActionLink,
CAL_LINK,
@ -25,6 +24,7 @@ import {
SubscriptionInterval,
useBillingBaseProductPricesQuery,
} from '~/generated/graphql';
import { isDefined } from 'twenty-shared/utils';
const StyledSubscriptionContainer = styled.div<{
withLongerMarginBottom: boolean;

View File

@ -22,8 +22,8 @@ import { TextInputV2 } from '@/ui/input/components/TextInputV2';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
import { Trans, useLingui } from '@lingui/react/macro';
import { isDefined } from 'twenty-shared';
import { OnboardingStatus } from '~/generated/graphql';
import { isDefined } from 'twenty-shared/utils';
const StyledContentContainer = styled.div`
width: 100%;

View File

@ -16,11 +16,11 @@ import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/Snac
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { TextInputV2 } from '@/ui/input/components/TextInputV2';
import { Trans, useLingui } from '@lingui/react/macro';
import { isDefined } from 'twenty-shared';
import {
OnboardingStatus,
useActivateWorkspaceMutation,
} from '~/generated/graphql';
import { isDefined } from 'twenty-shared/utils';
const StyledContentContainer = styled.div`
width: 100%;

View File

@ -29,10 +29,9 @@ import {
SeparatorLineText,
} from 'twenty-ui';
import { z } from 'zod';
import { isDefined } from 'twenty-shared';
import { OnboardingStatus } from '~/generated/graphql';
import { useCreateWorkspaceInvitation } from '../../modules/workspace-invitation/hooks/useCreateWorkspaceInvitation';
import { isDefined } from 'twenty-shared/utils';
const StyledAnimatedContainer = styled.div`
display: flex;

View File

@ -7,13 +7,13 @@ import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useSetRecoilState } from 'recoil';
import { isDefined } from 'twenty-shared';
import { AnimatedEaseIn, IconCheck, MainButton, RGBA } from 'twenty-ui';
import {
OnboardingStatus,
useGetCurrentUserLazyQuery,
} from '~/generated/graphql';
import { useNavigateApp } from '~/hooks/useNavigateApp';
import { isDefined } from 'twenty-shared/utils';
const StyledCheckContainer = styled.div`
align-items: center;

View File

@ -19,13 +19,13 @@ import { isMicrosoftCalendarEnabledState } from '@/client-config/states/isMicros
import { isMicrosoftMessagingEnabledState } from '@/client-config/states/isMicrosoftMessagingEnabledState';
import { useTriggerApisOAuth } from '@/settings/accounts/hooks/useTriggerApiOAuth';
import { AppPath } from '@/types/AppPath';
import { ConnectedAccountProvider } from 'twenty-shared';
import {
CalendarChannelVisibility,
MessageChannelVisibility,
OnboardingStatus,
useSkipSyncEmailOnboardingStepMutation,
} from '~/generated/graphql';
import { ConnectedAccountProvider } from 'twenty-shared/types';
const StyledSyncEmailsContainer = styled.div`
display: flex;

View File

@ -20,7 +20,6 @@ import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus';
import { isDefined } from 'twenty-shared';
import {
SubscriptionInterval,
SubscriptionStatus,
@ -28,6 +27,7 @@ import {
useUpdateBillingSubscriptionMutation,
} from '~/generated/graphql';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
import { isDefined } from 'twenty-shared/utils';
type SwitchInfo = {
newInterval: SubscriptionInterval;

View File

@ -36,7 +36,6 @@ import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
import { WorkspaceInviteLink } from '@/workspace/components/WorkspaceInviteLink';
import { WorkspaceInviteTeam } from '@/workspace/components/WorkspaceInviteTeam';
import { formatDistanceToNow } from 'date-fns';
import { isDefined } from 'twenty-shared';
import { useGetWorkspaceInvitationsQuery } from '~/generated/graphql';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
import { TableCell } from '../../modules/ui/layout/table/components/TableCell';
@ -44,6 +43,7 @@ import { TableRow } from '../../modules/ui/layout/table/components/TableRow';
import { useDeleteWorkspaceInvitation } from '../../modules/workspace-invitation/hooks/useDeleteWorkspaceInvitation';
import { useResendWorkspaceInvitation } from '../../modules/workspace-invitation/hooks/useResendWorkspaceInvitation';
import { workspaceInvitationsState } from '../../modules/workspace-invitation/states/workspaceInvitationsStates';
import { isDefined } from 'twenty-shared/utils';
const StyledButtonContainer = styled.div`
align-items: center;

View File

@ -20,7 +20,6 @@ import { useRecoilState, useRecoilValue } from 'recoil';
import { activeTabIdComponentState } from '@/ui/layout/tab/states/activeTabIdComponentState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useLingui } from '@lingui/react/macro';
import { isDefined } from 'twenty-shared';
import {
Button,
H3Title,
@ -37,6 +36,7 @@ import { useNavigateApp } from '~/hooks/useNavigateApp';
import { SETTINGS_OBJECT_DETAIL_TABS } from '~/pages/settings/data-model/constants/SettingsObjectDetailTabs';
import { updatedObjectNamePluralState } from '~/pages/settings/data-model/states/updatedObjectNamePluralState';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
import { isDefined } from 'twenty-shared/utils';
const StyledContentContainer = styled.div`
flex: 1;

View File

@ -34,11 +34,11 @@ import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/Snac
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
import { useLingui } from '@lingui/react/macro';
import { isDefined } from 'twenty-shared';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { useNavigateApp } from '~/hooks/useNavigateApp';
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
import { isDefined } from 'twenty-shared/utils';
//TODO: fix this type
type SettingsDataModelFieldEditFormValues = z.infer<

View File

@ -13,12 +13,12 @@ import { zodResolver } from '@hookform/resolvers/zod';
import { useEffect } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { useParams } from 'react-router-dom';
import { isDefined } from 'twenty-shared';
import { z } from 'zod';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { useNavigateApp } from '~/hooks/useNavigateApp';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
import { t } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
export const settingsDataModelFieldTypeFormSchema = z.object({
type: z.enum(

View File

@ -1,7 +1,7 @@
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { getFieldIdentifierType } from '@/settings/data-model/utils/getFieldIdentifierType';
import { isDefined } from 'twenty-shared';
import { isDefined } from 'twenty-shared/utils';
export const getSettingsObjectFieldType = (
objectMetadataItem: ObjectMetadataItem,

View File

@ -16,10 +16,10 @@ import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBa
import { useLingui } from '@lingui/react/macro';
import { useRecoilCallback } from 'recoil';
import { Key } from 'ts-key-enum';
import { isDefined } from 'twenty-shared';
import { useGenerateApiKeyTokenMutation } from '~/generated/graphql';
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
import { isDefined } from 'twenty-shared/utils';
export const SettingsDevelopersApiKeysNew = () => {
const { t } = useLingui();

View File

@ -31,9 +31,9 @@ import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBa
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { Trans, useLingui } from '@lingui/react/macro';
import { useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-shared';
import { FeatureFlagKey } from '~/generated/graphql';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
import { isDefined } from 'twenty-shared/utils';
const OBJECT_DROPDOWN_WIDTH = 340;
const ACTION_DROPDOWN_WIDTH = 140;

View File

@ -13,7 +13,6 @@ import { detectTimeFormat } from '@/localization/utils/detectTimeFormat';
import { detectTimeZone } from '@/localization/utils/detectTimeZone';
import { getWorkspaceDateFormatFromDateFormat } from '@/localization/utils/getWorkspaceDateFormatFromDateFormat';
import { getWorkspaceTimeFormatFromTimeFormat } from '@/localization/utils/getWorkspaceTimeFormatFromTimeFormat';
import { isDefined } from 'twenty-shared';
import {
WorkspaceMemberDateFormatEnum,
WorkspaceMemberTimeFormatEnum,
@ -23,6 +22,7 @@ import { DateTimeSettingsTimeFormatSelect } from '~/pages/settings/profile/appea
import { DateTimeSettingsTimeZoneSelect } from '~/pages/settings/profile/appearance/components/DateTimeSettingsTimeZoneSelect';
import { isEmptyObject } from '~/utils/isEmptyObject';
import { logError } from '~/utils/logError';
import { isDefined } from 'twenty-shared/utils';
const StyledContainer = styled.div`
display: flex;

View File

@ -2,8 +2,8 @@ import { detectTimeZone } from '@/localization/utils/detectTimeZone';
import { findAvailableTimeZoneOption } from '@/localization/utils/findAvailableTimeZoneOption';
import { AVAILABLE_TIMEZONE_OPTIONS } from '@/settings/accounts/constants/AvailableTimezoneOptions';
import { Select } from '@/ui/input/components/Select';
import { isDefined } from 'twenty-shared';
import { t } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
type DateTimeSettingsTimeZoneSelectProps = {
value?: string;

View File

@ -9,9 +9,10 @@ import { Select } from '@/ui/input/components/Select';
import { useRefreshObjectMetadataItems } from '@/object-metadata/hooks/useRefreshObjectMetadataItem';
import { useLingui } from '@lingui/react/macro';
import { APP_LOCALES, isDefined } from 'twenty-shared';
import { dynamicActivate } from '~/utils/i18n/dynamicActivate';
import { logError } from '~/utils/logError';
import { APP_LOCALES } from 'twenty-shared/translations';
import { isDefined } from 'twenty-shared/utils';
const StyledContainer = styled.div`
display: flex;

View File

@ -20,12 +20,12 @@ import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useState } from 'react';
import { useParams } from 'react-router-dom';
import { useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-shared';
import { IconCode, IconGauge, IconSettings, IconTestPipe } from 'twenty-ui';
import { useDebouncedCallback } from 'use-debounce';
import { FeatureFlagKey } from '~/generated/graphql';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
import { isDefined } from 'twenty-shared/utils';
const SERVERLESS_FUNCTION_DETAIL_ID = 'serverless-function-detail';

View File

@ -10,10 +10,10 @@ import { SettingsPath } from '@/types/SettingsPath';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useState } from 'react';
import { Key } from 'ts-key-enum';
import { isDefined } from 'twenty-shared';
import { useHotkeyScopeOnMount } from '~/hooks/useHotkeyScopeOnMount';
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
import { isDefined } from 'twenty-shared/utils';
export const SettingsServerlessFunctionsNew = () => {
const navigate = useNavigateSettings();

View File

@ -1,7 +1,6 @@
import { Meta, StoryObj } from '@storybook/react';
import { within } from '@storybook/test';
import { HttpResponse, graphql, http } from 'msw';
import { getImageAbsoluteURI } from 'twenty-shared';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
import { SettingsServerlessFunctionDetail } from '~/pages/settings/serverless-functions/SettingsServerlessFunctionDetail';
import {
@ -10,6 +9,7 @@ import {
} from '~/testing/decorators/PageDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks';
import { sleep } from '~/utils/sleep';
import { getImageAbsoluteURI } from 'twenty-shared/utils';
const SOURCE_CODE_FULL_PATH =
'serverless-function/20202020-1c25-4d02-bf25-6aeccf7ea419/adb4bd21-7670-4c81-9f74-1fc196fe87ea/source.ts';

View File

@ -6,7 +6,7 @@ import styled from '@emotion/styled';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { customDomainRecordsState } from '~/pages/settings/workspace/states/customDomainRecordsState';
import { useRecoilValue } from 'recoil';
import { capitalize } from 'twenty-shared';
import { capitalize } from 'twenty-shared/utils';
const StyledTable = styled(Table)`
background-color: ${({ theme }) => theme.background.transparent.lighter};

View File

@ -24,7 +24,7 @@ import { zodResolver } from '@hookform/resolvers/zod';
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
import { SettingsPath } from '@/types/SettingsPath';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
import { isDefined } from 'twenty-shared';
import { isDefined } from 'twenty-shared/utils';
export const SettingsDomain = () => {
const navigate = useNavigateSettings();

View File

@ -6,8 +6,8 @@ import { H2Title, Section } from 'twenty-ui';
import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState';
import { useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-shared';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { isDefined } from 'twenty-shared/utils';
const StyledDomainFormWrapper = styled.div`
align-items: center;

View File

@ -1,7 +1,7 @@
import { customDomainRecordsState } from '~/pages/settings/workspace/states/customDomainRecordsState';
import { useCheckCustomDomainValidRecordsMutation } from '~/generated/graphql';
import { isDefined } from 'twenty-shared';
import { useSetRecoilState } from 'recoil';
import { isDefined } from 'twenty-shared/utils';
export const useCheckCustomDomainValidRecords = () => {
const [checkCustomDomainValidRecords] =