From 3f0c09a7ee65b7795289069b0d92dac09cc2f50e Mon Sep 17 00:00:00 2001 From: martmull Date: Wed, 11 Sep 2024 12:14:06 +0200 Subject: [PATCH 01/30] Add process.env.VAR resolution in editor --- .../code-editor/components/CodeEditor.tsx | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/twenty-front/src/modules/ui/input/code-editor/components/CodeEditor.tsx b/packages/twenty-front/src/modules/ui/input/code-editor/components/CodeEditor.tsx index 3c24d62cdef8..a00462a59f62 100644 --- a/packages/twenty-front/src/modules/ui/input/code-editor/components/CodeEditor.tsx +++ b/packages/twenty-front/src/modules/ui/input/code-editor/components/CodeEditor.tsx @@ -51,6 +51,27 @@ export const CodeEditor = ({ monaco.editor.setTheme('codeEditorTheme'); if (language === 'typescript') { + const environmentVariables = {}; + + const environmentDefinition = ` + declare namespace NodeJS { + interface ProcessEnv { + ${Object.keys(environmentVariables) + .map((key) => `${key}: string;`) + .join('\n')} + } + } + + declare const process: { + env: NodeJS.ProcessEnv; + }; + `; + + monaco.languages.typescript.typescriptDefaults.addExtraLib( + environmentDefinition, + 'ts:process-env.d.ts', + ); + await AutoTypings.create(editor, { monaco, preloadPackages: true, From a380ad63000f1871851149a0139bebdf158ff95b Mon Sep 17 00:00:00 2001 From: martmull Date: Fri, 13 Sep 2024 10:45:03 +0200 Subject: [PATCH 02/30] Use models for monaco editor --- ...ettingsServerlessFunctionCodeEditorTab.tsx | 9 ++- .../SettingsServerlessFunctionTestTab.tsx | 4 +- .../constants/DefaultCode.ts | 8 +++ .../code-editor/components/CodeEditor.tsx | 68 +++++++++++-------- .../SettingsServerlessFunctionsNew.tsx | 2 +- ...ttingsServerlessFunctionDetail.stories.tsx | 2 +- 6 files changed, 59 insertions(+), 34 deletions(-) create mode 100644 packages/twenty-front/src/modules/settings/serverless-functions/constants/DefaultCode.ts diff --git a/packages/twenty-front/src/modules/settings/serverless-functions/components/tabs/SettingsServerlessFunctionCodeEditorTab.tsx b/packages/twenty-front/src/modules/settings/serverless-functions/components/tabs/SettingsServerlessFunctionCodeEditorTab.tsx index 6329eb7ac538..3a1773895410 100644 --- a/packages/twenty-front/src/modules/settings/serverless-functions/components/tabs/SettingsServerlessFunctionCodeEditorTab.tsx +++ b/packages/twenty-front/src/modules/settings/serverless-functions/components/tabs/SettingsServerlessFunctionCodeEditorTab.tsx @@ -95,6 +95,13 @@ export const SettingsServerlessFunctionCodeEditorTab = ({ }, SettingsServerlessFunctionHotkeyScope.ServerlessFunctionEditorTab, ); + + const file = { + path: 'src/index.ts', + language: 'typescript', + content: formValues.code, + }; + return (
diff --git a/packages/twenty-front/src/modules/settings/serverless-functions/constants/DefaultCode.ts b/packages/twenty-front/src/modules/settings/serverless-functions/constants/DefaultCode.ts new file mode 100644 index 000000000000..d416b21cc6ba --- /dev/null +++ b/packages/twenty-front/src/modules/settings/serverless-functions/constants/DefaultCode.ts @@ -0,0 +1,8 @@ +export const DEFAULT_CODE = `export const handler = async ( + event: object, + context: object +): Promise => { + // Your code here + return {}; +} +`; diff --git a/packages/twenty-front/src/modules/ui/input/code-editor/components/CodeEditor.tsx b/packages/twenty-front/src/modules/ui/input/code-editor/components/CodeEditor.tsx index a00462a59f62..d16062cc2463 100644 --- a/packages/twenty-front/src/modules/ui/input/code-editor/components/CodeEditor.tsx +++ b/packages/twenty-front/src/modules/ui/input/code-editor/components/CodeEditor.tsx @@ -4,19 +4,9 @@ import { editor, MarkerSeverity } from 'monaco-editor'; import { codeEditorTheme } from '@/ui/input/code-editor/theme/CodeEditorTheme'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; -import { useEffect } from 'react'; import { useGetAvailablePackages } from '@/settings/serverless-functions/hooks/useGetAvailablePackages'; import { isDefined } from '~/utils/isDefined'; -export const DEFAULT_CODE = `export const handler = async ( - event: object, - context: object -): Promise => { - // Your code here - return {}; -} -`; - const StyledEditor = styled(Editor)` border: 1px solid ${({ theme }) => theme.border.color.medium}; border-top: none; @@ -24,17 +14,25 @@ const StyledEditor = styled(Editor)` ${({ theme }) => theme.border.radius.sm}; `; +type File = { + path: string; + language: string; + content: string; +}; + type CodeEditorProps = Omit & { + file?: File; header: React.ReactNode; onChange?: (value: string) => void; setIsCodeValid?: (isCodeValid: boolean) => void; }; export const CodeEditor = ({ - value = DEFAULT_CODE, + file, + value, + language, onChange, setIsCodeValid, - language = 'typescript', height = 450, options = undefined, header, @@ -50,7 +48,31 @@ export const CodeEditor = ({ monaco.editor.defineTheme('codeEditorTheme', codeEditorTheme(theme)); monaco.editor.setTheme('codeEditorTheme'); - if (language === 'typescript') { + if (!isDefined(file)) { + return; + } + + const model = monaco.editor.createModel( + file.content, + file.language, + monaco.Uri.file(file.path), + ); + editor.setModel(model); + + if (file.language === 'typescript') { + monaco.languages.typescript.typescriptDefaults.setCompilerOptions({ + ...monaco.languages.typescript.typescriptDefaults.getCompilerOptions(), + moduleResolution: + monaco.languages.typescript.ModuleResolutionKind.NodeJs, + baseUrl: 'file:///src', + paths: { + 'src/*': ['file:///src/*'], + }, + allowSyntheticDefaultImports: true, + esModuleInterop: true, + noEmit: true, + target: monaco.languages.typescript.ScriptTarget.ESNext, + }); const environmentVariables = {}; const environmentDefinition = ` @@ -78,6 +100,7 @@ export const CodeEditor = ({ onlySpecifiedPackages: true, versions: availablePackages, debounceDuration: 0, + fileRootPath: 'file:///', }); } }; @@ -92,27 +115,14 @@ export const CodeEditor = ({ setIsCodeValid?.(true); }; - useEffect(() => { - const style = document.createElement('style'); - style.innerHTML = ` - .monaco-editor .margin .line-numbers { - font-weight: bold; - } - `; - document.head.appendChild(style); - return () => { - document.head.removeChild(style); - }; - }, []); - return ( isDefined(availablePackages) && ( - <> +
{header} value && onChange?.(value)} onValidate={handleEditorValidation} @@ -128,7 +138,7 @@ export const CodeEditor = ({ }, }} /> - +
) ); }; diff --git a/packages/twenty-front/src/pages/settings/serverless-functions/SettingsServerlessFunctionsNew.tsx b/packages/twenty-front/src/pages/settings/serverless-functions/SettingsServerlessFunctionsNew.tsx index 2011d88a9552..15806ff59988 100644 --- a/packages/twenty-front/src/pages/settings/serverless-functions/SettingsServerlessFunctionsNew.tsx +++ b/packages/twenty-front/src/pages/settings/serverless-functions/SettingsServerlessFunctionsNew.tsx @@ -10,13 +10,13 @@ import { ServerlessFunctionNewFormValues } from '@/settings/serverless-functions import { SettingsServerlessFunctionHotkeyScope } from '@/settings/serverless-functions/types/SettingsServerlessFunctionHotKeyScope'; import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { SettingsPath } from '@/types/SettingsPath'; -import { DEFAULT_CODE } from '@/ui/input/code-editor/components/CodeEditor'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { useState } from 'react'; import { Key } from 'ts-key-enum'; import { IconFunction } from 'twenty-ui'; import { useHotkeyScopeOnMount } from '~/hooks/useHotkeyScopeOnMount'; import { isDefined } from '~/utils/isDefined'; +import { DEFAULT_CODE } from '~/modules/settings/serverless-functions/constants/DefaultCode'; export const SettingsServerlessFunctionsNew = () => { const navigate = useNavigate(); diff --git a/packages/twenty-front/src/pages/settings/serverless-functions/__stories__/SettingsServerlessFunctionDetail.stories.tsx b/packages/twenty-front/src/pages/settings/serverless-functions/__stories__/SettingsServerlessFunctionDetail.stories.tsx index 8543e0376a6e..9ac2cb31025f 100644 --- a/packages/twenty-front/src/pages/settings/serverless-functions/__stories__/SettingsServerlessFunctionDetail.stories.tsx +++ b/packages/twenty-front/src/pages/settings/serverless-functions/__stories__/SettingsServerlessFunctionDetail.stories.tsx @@ -1,4 +1,3 @@ -import { DEFAULT_CODE } from '@/ui/input/code-editor/components/CodeEditor'; import { Meta, StoryObj } from '@storybook/react'; import { within } from '@storybook/test'; import { graphql, http, HttpResponse } from 'msw'; @@ -10,6 +9,7 @@ import { import { graphqlMocks } from '~/testing/graphqlMocks'; import { getImageAbsoluteURI } from '~/utils/image/getImageAbsoluteURI'; import { sleep } from '~/utils/sleep'; +import { DEFAULT_CODE } from '~/modules/settings/serverless-functions/constants/DefaultCode'; const SOURCE_CODE_FULL_PATH = 'serverless-function/20202020-1c25-4d02-bf25-6aeccf7ea419/adb4bd21-7670-4c81-9f74-1fc196fe87ea/source.ts'; From d20f4b9133c73a61a24d41b785da86fdf0dbb6da Mon Sep 17 00:00:00 2001 From: martmull Date: Fri, 13 Sep 2024 13:03:27 +0200 Subject: [PATCH 03/30] Add .env file --- ...ettingsServerlessFunctionCodeEditorTab.tsx | 35 ++++-- .../SettingsServerlessFunctionTestTab.tsx | 12 +- ...ngsServerlessFunctionTabListComponentId.ts | 2 + .../code-editor/components/CodeEditor.tsx | 112 ++++++++++-------- 4 files changed, 97 insertions(+), 64 deletions(-) create mode 100644 packages/twenty-front/src/modules/settings/serverless-functions/constants/SettingsServerlessFunctionTabListComponentId.ts diff --git a/packages/twenty-front/src/modules/settings/serverless-functions/components/tabs/SettingsServerlessFunctionCodeEditorTab.tsx b/packages/twenty-front/src/modules/settings/serverless-functions/components/tabs/SettingsServerlessFunctionCodeEditorTab.tsx index 3a1773895410..86a33cbf92ea 100644 --- a/packages/twenty-front/src/modules/settings/serverless-functions/components/tabs/SettingsServerlessFunctionCodeEditorTab.tsx +++ b/packages/twenty-front/src/modules/settings/serverless-functions/components/tabs/SettingsServerlessFunctionCodeEditorTab.tsx @@ -13,6 +13,9 @@ import { useNavigate } from 'react-router-dom'; import { Key } from 'ts-key-enum'; import { H2Title, IconGitCommit, IconPlayerPlay, IconRestore } from 'twenty-ui'; import { useHotkeyScopeOnMount } from '~/hooks/useHotkeyScopeOnMount'; +import { SETTINGS_SERVERLESS_FUNCTION_TAB_LIST_COMPONENT_ID } from '@/settings/serverless-functions/constants/SettingsServerlessFunctionTabListComponentId'; +import { useTabList } from '@/ui/layout/tab/hooks/useTabList'; +import { useRecoilValue } from 'recoil'; const StyledTabList = styled(TabList)` border-bottom: none; @@ -37,6 +40,10 @@ export const SettingsServerlessFunctionCodeEditorTab = ({ onChange: (key: string) => (value: string) => void; setIsCodeValid: (isCodeValid: boolean) => void; }) => { + const { activeTabIdState } = useTabList( + SETTINGS_SERVERLESS_FUNCTION_TAB_LIST_COMPONENT_ID, + ); + const activeTabId = useRecoilValue(activeTabIdState); const TestButton = (