Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/bruno-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"mime-types": "^3.0.2",
"moment": "^2.30.1",
"moment-timezone": "^0.5.47",
"monaco-editor": "^0.55.1",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how ScriptEditor imports MonacoEditor — static vs dynamic.
fd -t f 'index.js' packages/bruno-app/src/components/ScriptEditor | xargs rg -n -C3 -e "MonacoEditor" -e "React\.lazy" -e "import\("

Repository: usebruno/bruno

Length of output: 469


🏁 Script executed:

fd -t f 'index.js' packages/bruno-app/src/components/MonacoEditor | head -5 | xargs cat -n

Repository: usebruno/bruno

Length of output: 8586


🏁 Script executed:

rg -n "React\.lazy|dynamic.*import|import.*monaco" packages/bruno-app/src/components/MonacoEditor --type js --type jsx

Repository: usebruno/bruno

Length of output: 85


Lazy-load Monaco behind the beta flag.

monaco-editor is statically imported in MonacoEditor/index.js (line 3), which means the entire ~2MB minified bundle loads for all users on app startup—even though the feature is opt-in beta only. Currently, ScriptEditor conditionally renders either MonacoEditor or CodeEditor, but this conditional happens after the static import has already pulled the bundle.

Move the MonacoEditor import in ScriptEditor/index.js to use React.lazy(), and defer loading until the beta flag is actually enabled:

const MonacoEditor = lazy(() => import('components/MonacoEditor'));
const Editor = useMonaco ? <Suspense fallback={...}><MonacoEditor {...props} /></Suspense> : <CodeEditor {...props} />;

This prevents non-beta users from incurring the bundle cost on initial load.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bruno-app/package.json` at line 63, MonacoEditor is currently
statically imported causing the ~2MB bundle to load for all users; change the
import in ScriptEditor (where useMonaco/beta flag is checked) to lazy-load the
component instead of importing it from MonacoEditor/index.js at module
top-level: replace the static import with React.lazy(() =>
import('components/MonacoEditor')) and render it only when the beta
flag/useMonaco is true inside a Suspense fallback, otherwise render CodeEditor;
ensure no other files keep a top-level import of MonacoEditor so the bundle is
deferred until the component is actually needed.

"mousetrap": "^1.6.5",
"nanoid": "3.3.8",
"path": "^0.12.7",
Expand Down
10 changes: 10 additions & 0 deletions packages/bruno-app/rsbuild.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ export default defineConfig({
tools: {
rspack: {
module: {
rules: [
{
test: /monaco-editor\/esm\/vs\/.*\.worker/,
parser: {
javascript: {
dynamicImportMode: 'lazy'
}
}
},
],
parser: {
javascript: {
// This loads the JavaScript contents from a library along with the main JavaScript bundle.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useEffect, useRef } from 'react';
import get from 'lodash/get';
import find from 'lodash/find';
import { useDispatch, useSelector } from 'react-redux';
import CodeEditor from 'components/CodeEditor';
import ScriptEditor from 'components/ScriptEditor';
import { updateCollectionRequestScript, updateCollectionResponseScript } from 'providers/ReduxStore/slices/collections';
import { saveCollectionSettings } from 'providers/ReduxStore/slices/collections/actions';
import { updateScriptPaneTab } from 'providers/ReduxStore/slices/tabs';
Expand Down Expand Up @@ -100,7 +100,7 @@ const Script = ({ collection }) => {
</TabsList>

<TabsContent value="pre-request" className="mt-2">
<CodeEditor
<ScriptEditor
ref={preRequestEditorRef}
collection={collection}
value={requestScript || ''}
Expand All @@ -115,7 +115,7 @@ const Script = ({ collection }) => {
</TabsContent>

<TabsContent value="post-response" className="mt-2">
<CodeEditor
<ScriptEditor
ref={postResponseEditorRef}
collection={collection}
value={responseScript || ''}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import get from 'lodash/get';
import { useDispatch, useSelector } from 'react-redux';
import CodeEditor from 'components/CodeEditor';
import ScriptEditor from 'components/ScriptEditor';
import { updateCollectionTests } from 'providers/ReduxStore/slices/collections';
import { saveCollectionSettings } from 'providers/ReduxStore/slices/collections/actions';
import { useTheme } from 'providers/Theme';
Expand Down Expand Up @@ -29,7 +29,7 @@ const Tests = ({ collection }) => {
return (
<StyledWrapper className="w-full flex flex-col h-full">
<div className="text-xs mb-4 text-muted">These tests will run any time a request in this collection is sent.</div>
<CodeEditor
<ScriptEditor
collection={collection}
value={tests || ''}
theme={displayedTheme}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useEffect, useRef } from 'react';
import get from 'lodash/get';
import find from 'lodash/find';
import { useDispatch, useSelector } from 'react-redux';
import CodeEditor from 'components/CodeEditor';
import ScriptEditor from 'components/ScriptEditor';
import { updateFolderRequestScript, updateFolderResponseScript } from 'providers/ReduxStore/slices/collections';
import { saveFolderRoot } from 'providers/ReduxStore/slices/collections/actions';
import { updateScriptPaneTab } from 'providers/ReduxStore/slices/tabs';
Expand Down Expand Up @@ -103,9 +103,10 @@ const Script = ({ collection, folder }) => {
</TabsList>

<TabsContent value="pre-request" className="mt-2">
<CodeEditor
<ScriptEditor
ref={preRequestEditorRef}
collection={collection}
item={folder}
value={requestScript || ''}
theme={displayedTheme}
onEdit={onRequestScriptEdit}
Expand All @@ -118,9 +119,10 @@ const Script = ({ collection, folder }) => {
</TabsContent>

<TabsContent value="post-response" className="mt-2">
<CodeEditor
<ScriptEditor
ref={postResponseEditorRef}
collection={collection}
item={folder}
value={responseScript || ''}
theme={displayedTheme}
onEdit={onResponseScriptEdit}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import get from 'lodash/get';
import { useDispatch, useSelector } from 'react-redux';
import CodeEditor from 'components/CodeEditor';
import ScriptEditor from 'components/ScriptEditor';
import { updateFolderTests } from 'providers/ReduxStore/slices/collections';
import { saveFolderRoot } from 'providers/ReduxStore/slices/collections/actions';
import { useTheme } from 'providers/Theme';
Expand Down Expand Up @@ -30,8 +30,9 @@ const Tests = ({ collection, folder }) => {
return (
<StyledWrapper className="w-full flex flex-col h-full">
<div className="text-xs mb-4 text-muted">These tests will run any time a request in this collection is sent.</div>
<CodeEditor
<ScriptEditor
collection={collection}
item={folder}
value={tests || ''}
theme={displayedTheme}
onEdit={onEdit}
Expand Down
41 changes: 41 additions & 0 deletions packages/bruno-app/src/components/MonacoEditor/StyledWrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import styled from 'styled-components';

const StyledWrapper = styled.div`
width: 100%;
height: 100%;
display: flex;
flex-direction: column;

.monaco-editor-container {
flex: 1 1 0;
min-height: 0;
border: solid 1px ${(props) => props.theme.codemirror.border};
background: ${(props) => props.theme.codemirror.bg};
}

/* Flush line numbers to the left edge like CodeMirror */
.monaco-editor .margin-view-overlays .line-numbers {
text-align: left !important;
padding-left: 3px !important;
}

/* Bruno variable highlighting decorations */
.bruno-variable-valid {
color: ${(props) => props.theme.codemirror.variable.valid} !important;
font-weight: 500;
}

.bruno-variable-invalid {
color: ${(props) => props.theme.codemirror.variable.invalid} !important;
font-weight: 500;
text-decoration: wavy underline ${(props) => props.theme.codemirror.variable.invalid};
text-underline-offset: 3px;
}

.bruno-variable-prompt {
color: ${(props) => props.theme.codemirror.variable.prompt} !important;
font-weight: 500;
}
`;

export default StyledWrapper;
Loading
Loading