From 4a4481a26f3ec18e96aa4aaba89db00f94f60fa0 Mon Sep 17 00:00:00 2001 From: Sanjai Kumar Date: Thu, 21 Nov 2024 12:00:25 +0530 Subject: [PATCH 01/20] feat: add 'isNotEmpty' assertion operator --- .../Assertions/AssertionOperator/index.js | 2 ++ .../RequestPane/Assertions/AssertionRow/index.js | 4 ++++ packages/bruno-js/src/runtime/assert-runtime.js | 12 ++++++++++++ 3 files changed, 18 insertions(+) diff --git a/packages/bruno-app/src/components/RequestPane/Assertions/AssertionOperator/index.js b/packages/bruno-app/src/components/RequestPane/Assertions/AssertionOperator/index.js index 1bc1c3f88..523fcf73d 100644 --- a/packages/bruno-app/src/components/RequestPane/Assertions/AssertionOperator/index.js +++ b/packages/bruno-app/src/components/RequestPane/Assertions/AssertionOperator/index.js @@ -20,6 +20,7 @@ import React from 'react'; * endsWith : ends with * between : between * isEmpty : is empty + * isNotEmpty : is not empty * isNull : is null * isUndefined : is undefined * isDefined : is defined @@ -51,6 +52,7 @@ const AssertionOperator = ({ operator, onChange }) => { 'endsWith', 'between', 'isEmpty', + 'isNotEmpty', 'isNull', 'isUndefined', 'isDefined', diff --git a/packages/bruno-app/src/components/RequestPane/Assertions/AssertionRow/index.js b/packages/bruno-app/src/components/RequestPane/Assertions/AssertionRow/index.js index 38250f8ea..953db9e8d 100644 --- a/packages/bruno-app/src/components/RequestPane/Assertions/AssertionRow/index.js +++ b/packages/bruno-app/src/components/RequestPane/Assertions/AssertionRow/index.js @@ -24,6 +24,7 @@ import { useTheme } from 'providers/Theme'; * endsWith : ends with * between : between * isEmpty : is empty + * isNotEmpty : is not empty * isNull : is null * isUndefined : is undefined * isDefined : is defined @@ -61,6 +62,7 @@ const parseAssertionOperator = (str = '') => { 'endsWith', 'between', 'isEmpty', + 'isNotEmpty', 'isNull', 'isUndefined', 'isDefined', @@ -75,6 +77,7 @@ const parseAssertionOperator = (str = '') => { const unaryOperators = [ 'isEmpty', + 'isNotEmpty', 'isNull', 'isUndefined', 'isDefined', @@ -113,6 +116,7 @@ const parseAssertionOperator = (str = '') => { const isUnaryOperator = (operator) => { const unaryOperators = [ 'isEmpty', + 'isNotEmpty', 'isNull', 'isUndefined', 'isDefined', diff --git a/packages/bruno-js/src/runtime/assert-runtime.js b/packages/bruno-js/src/runtime/assert-runtime.js index b338730cc..d62bd22bf 100644 --- a/packages/bruno-js/src/runtime/assert-runtime.js +++ b/packages/bruno-js/src/runtime/assert-runtime.js @@ -58,6 +58,7 @@ chai.use(function (chai, utils) { * endsWith : ends with * between : between * isEmpty : is empty + * isNotEmpty : is not empty * isNull : is null * isUndefined : is undefined * isDefined : is defined @@ -95,6 +96,7 @@ const parseAssertionOperator = (str = '') => { 'endsWith', 'between', 'isEmpty', + 'isNotEmpty', 'isNull', 'isUndefined', 'isDefined', @@ -109,6 +111,7 @@ const parseAssertionOperator = (str = '') => { const unaryOperators = [ 'isEmpty', + 'isNotEmpty', 'isNull', 'isUndefined', 'isDefined', @@ -147,6 +150,7 @@ const parseAssertionOperator = (str = '') => { const isUnaryOperator = (operator) => { const unaryOperators = [ 'isEmpty', + 'isNotEmpty', 'isNull', 'isUndefined', 'isDefined', @@ -345,6 +349,14 @@ class AssertRuntime { case 'isEmpty': expect(lhs).to.be.empty; break; + case 'isNotEmpty': + expect(lhs).to.not.be.oneOf([null, 0, false, '', []]); + if (Array.isArray(lhs)) { + expect(lhs).to.have.length.above(0); + } else if (typeof lhs === 'object' && lhs !== null) { + expect(Object.keys(lhs)).to.have.length.above(0); + } + break; case 'isNull': expect(lhs).to.be.null; break; From f110d898f5ad450d283d1c7e4978a86189f7c3aa Mon Sep 17 00:00:00 2001 From: Sanjai Kumar Date: Tue, 3 Dec 2024 18:01:22 +0530 Subject: [PATCH 02/20] fix: refactor createFormData function --- .../src/ipc/network/prepare-request.js | 42 +++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/packages/bruno-electron/src/ipc/network/prepare-request.js b/packages/bruno-electron/src/ipc/network/prepare-request.js index c8b36bb89..80c3ee64d 100644 --- a/packages/bruno-electron/src/ipc/network/prepare-request.js +++ b/packages/bruno-electron/src/ipc/network/prepare-request.js @@ -1,5 +1,5 @@ const os = require('os'); -const { get, each, filter, compact, forOwn } = require('lodash'); +const { get, each, filter, compact, forEach } = require('lodash'); const decomment = require('decomment'); const FormData = require('form-data'); const fs = require('fs'); @@ -166,26 +166,34 @@ const mergeFolderLevelScripts = (request, requestTreePath, scriptFlow) => { } }; -const createFormData = (datas, collectionPath) => { +const createFormData = (data, collectionPath) => { // make axios work in node using form data // reference: https://github.com/axios/axios/issues/1006#issuecomment-320165427 const form = new FormData(); - forOwn(datas, (value, key) => { - if (typeof value == 'string') { - form.append(key, value); + forEach(data, (datum) => { + const { name, type, value } = datum; + + if (type === 'text') { + if (Array.isArray(value)) { + value.forEach((val) => form.append(name, val)); + } else { + form.append(name, value); + } return; } - const filePaths = value || []; - filePaths?.forEach?.((filePath) => { - let trimmedFilePath = filePath.trim(); + if (type === 'file') { + const filePaths = value || []; + filePaths.forEach((filePath) => { + let trimmedFilePath = filePath.trim(); - if (!path.isAbsolute(trimmedFilePath)) { - trimmedFilePath = path.join(collectionPath, trimmedFilePath); - } + if (!path.isAbsolute(trimmedFilePath)) { + trimmedFilePath = path.join(collectionPath, trimmedFilePath); + } - form.append(key, fs.createReadStream(trimmedFilePath), path.basename(trimmedFilePath)); - }); + form.append(name, fs.createReadStream(trimmedFilePath), path.basename(trimmedFilePath)); + }); + } }); return form; }; @@ -435,11 +443,11 @@ const prepareRequest = (item, collection) => { } if (request.body.mode === 'multipartForm') { - axiosRequest.headers['content-type'] = 'multipart/form-data'; - const params = {}; + if (!contentTypeDefined) { + axiosRequest.headers['content-type'] = 'multipart/form-data'; + } const enabledParams = filter(request.body.multipartForm, (p) => p.enabled); - each(enabledParams, (p) => (params[p.name] = p.value)); - axiosRequest.data = params; + axiosRequest.data = createFormData(enabledParams); } if (request.body.mode === 'graphql') { From ea1f385d1cd8f5d8b0ed7cde4134193ea7abb97d Mon Sep 17 00:00:00 2001 From: lohit Date: Sat, 14 Dec 2024 18:55:28 +0530 Subject: [PATCH 03/20] fix: rename folder case insensitive (#3635) * fix: rename folder case insensitive * fix: add hasSubFolders utility and update folder moving logic for Windows OS * fix: validations * fix: updates * fix: updates --------- Co-authored-by: Pragadesh-45 --- packages/bruno-electron/src/app/watcher.js | 31 +++++++++++++++++++ packages/bruno-electron/src/ipc/collection.js | 27 +++++++++++++--- .../bruno-electron/src/utils/filesystem.js | 9 +++++- 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/packages/bruno-electron/src/app/watcher.js b/packages/bruno-electron/src/app/watcher.js index a26ff5d53..1b2e19d4a 100644 --- a/packages/bruno-electron/src/app/watcher.js +++ b/packages/bruno-electron/src/app/watcher.js @@ -44,6 +44,13 @@ const isCollectionRootBruFile = (pathname, collectionPath) => { return dirname === collectionPath && basename === 'collection.bru'; }; +const isPathInside = (childPath, parentPath) => { + const absoluteChildPath = path.resolve(childPath); + const absoluteParentPath = path.resolve(parentPath); + return absoluteChildPath.startsWith(absoluteParentPath); +} + + const hydrateRequestWithUuid = (request, pathname) => { request.uid = getRequestUid(pathname); @@ -506,6 +513,30 @@ class Watcher { this.watchers[watchPath] = null; } } + + getWatcherForPath(_path) { + const paths = Object.keys(this.watchers); + return paths.find(parentFolder => isPathInside(_path, parentFolder)); + } + + unlink(_path) { + const watcherPath = this.getWatcherForPath(_path); + if (watcherPath) { + const _watcher = this.watchers[watcherPath]; + _watcher.unwatch(_path); + } + } + + add(_path) { + const watcherPath = this.getWatcherForPath(_path); + if (watcherPath) { + const _watcher = this.watchers[watcherPath]; + if (!_watcher?.has?.(_path)) { + _watcher?.add?.(_path); + } + } + } + } module.exports = Watcher; diff --git a/packages/bruno-electron/src/ipc/collection.js b/packages/bruno-electron/src/ipc/collection.js index 130228797..2c4ff3a84 100644 --- a/packages/bruno-electron/src/ipc/collection.js +++ b/packages/bruno-electron/src/ipc/collection.js @@ -21,7 +21,8 @@ const { normalizeAndResolvePath, safeToRename, isWindowsOS, - isValidFilename + isValidFilename, + hasSubFolders, } = require('../utils/filesystem'); const { openCollectionDialog } = require('../app/collections'); const { generateUidBasedOnHash, stringifyJson, safeParseJSON, safeStringifyJSON } = require('../utils/common'); @@ -343,6 +344,7 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection // rename item ipcMain.handle('renderer:rename-item', async (event, oldPath, newPath, newName) => { + const tempDir = path.join(os.tmpdir(), `temp-folder-${Date.now()}`); try { // Normalize paths if they are WSL paths oldPath = isWSLPath(oldPath) ? normalizeWslPath(oldPath) : normalizeAndResolvePath(oldPath); @@ -365,15 +367,18 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection moveRequestUid(bruFile, newBruFilePath); } - if (isWindowsOS() && !isWSLPath(oldPath)) { - const tempDir = path.join(os.tmpdir(), `temp-folder-${Date.now()}`); + watcher.unlink(path.dirname(oldPath)); + if (isWindowsOS() && !isWSLPath(oldPath) && hasSubFolders(oldPath)) { + console.log('Windows OS: Moving folder with subfolders'); await fsExtra.copy(oldPath, tempDir); - await fsExtra.move(tempDir, newPath, { overwrite: true }); await fsExtra.remove(oldPath); + await fsExtra.move(tempDir, newPath, { overwrite: true }); + await fsExtra.remove(tempDir); } else { await fs.renameSync(oldPath, newPath); } + watcher.add(path.dirname(newPath)); return newPath; } @@ -397,6 +402,20 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection return newPath; } catch (error) { + // add path back to watcher in case an error during the rename file operations + // adds unlinked path back to watcher if doesn't exist + watcher.add(path.dirname(oldPath)); + + if (isWindowsOS() && !isWSLPath(oldPath)) { + if (fsExtra.pathExistsSync(tempDir) && !fsExtra.pathExistsSync(oldPath)) { + try { + await fsExtra.copy(tempDir, oldPath); + await fsExtra.remove(tempDir); + } catch (err) { + console.error("Failed to restore data to the old path:", err); + } + } + } return Promise.reject(error); } }); diff --git a/packages/bruno-electron/src/utils/filesystem.js b/packages/bruno-electron/src/utils/filesystem.js index aeb0bdd96..bd4e0aec0 100644 --- a/packages/bruno-electron/src/utils/filesystem.js +++ b/packages/bruno-electron/src/utils/filesystem.js @@ -38,6 +38,12 @@ const isDirectory = (dirPath) => { } }; +const hasSubFolders = (dir) => { + console.log('Checking for subfolders in:', dir); + const files = fs.readdirSync(dir); + return files.some(file => fs.statSync(path.join(dir, file)).isDirectory()); +}; + const normalizeAndResolvePath = (pathname) => { if (isSymbolicLink(pathname)) { const absPath = path.dirname(pathname); @@ -224,5 +230,6 @@ module.exports = { sanitizeDirectoryName, isWindowsOS, safeToRename, - isValidFilename + isValidFilename, + hasSubFolders }; From 3efcdf254ece8bd758abad3f8b23946a04f78f83 Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Sat, 14 Dec 2024 19:42:39 +0530 Subject: [PATCH 04/20] fix(#3627): fix rename issues on windows os --- packages/bruno-electron/src/app/watcher.js | 44 +++++++++---------- packages/bruno-electron/src/ipc/collection.js | 41 ++++++++++++----- .../bruno-electron/src/utils/filesystem.js | 5 +-- 3 files changed, 54 insertions(+), 36 deletions(-) diff --git a/packages/bruno-electron/src/app/watcher.js b/packages/bruno-electron/src/app/watcher.js index 1b2e19d4a..43d01153d 100644 --- a/packages/bruno-electron/src/app/watcher.js +++ b/packages/bruno-electron/src/app/watcher.js @@ -44,13 +44,6 @@ const isCollectionRootBruFile = (pathname, collectionPath) => { return dirname === collectionPath && basename === 'collection.bru'; }; -const isPathInside = (childPath, parentPath) => { - const absoluteChildPath = path.resolve(childPath); - const absoluteParentPath = path.resolve(parentPath); - return absoluteChildPath.startsWith(absoluteParentPath); -} - - const hydrateRequestWithUuid = (request, pathname) => { request.uid = getRequestUid(pathname); @@ -396,6 +389,8 @@ const change = async (win, pathname, collectionUid, collectionPath) => { }; const unlink = (win, pathname, collectionUid, collectionPath) => { + console.log(`watcher unlink: ${pathname}`); + if (isBruEnvironmentConfig(pathname, collectionPath)) { return unlinkEnvironmentFile(win, pathname, collectionUid); } @@ -514,29 +509,32 @@ class Watcher { } } - getWatcherForPath(_path) { + getWatcherByItemPath(itemPath) { const paths = Object.keys(this.watchers); - return paths.find(parentFolder => isPathInside(_path, parentFolder)); + + const watcherPath = paths?.find(collectionPath => { + const absCollectionPath = path.resolve(collectionPath); + const absItemPath = path.resolve(itemPath); + + return absItemPath.startsWith(absCollectionPath); + }); + + return watcherPath ? this.watchers[watcherPath] : null; } - - unlink(_path) { - const watcherPath = this.getWatcherForPath(_path); - if (watcherPath) { - const _watcher = this.watchers[watcherPath]; - _watcher.unwatch(_path); + + unlinkItemPathInWatcher(itemPath) { + const watcher = this.getWatcherByItemPath(itemPath); + if (watcher) { + watcher.unwatch(itemPath); } } - add(_path) { - const watcherPath = this.getWatcherForPath(_path); - if (watcherPath) { - const _watcher = this.watchers[watcherPath]; - if (!_watcher?.has?.(_path)) { - _watcher?.add?.(_path); - } + addItemPathInWatcher(itemPath) { + const watcher = this.getWatcherByItemPath(itemPath); + if (watcher && !watcher?.has?.(itemPath)) { + watcher?.add?.(itemPath); } } - } module.exports = Watcher; diff --git a/packages/bruno-electron/src/ipc/collection.js b/packages/bruno-electron/src/ipc/collection.js index 2c4ff3a84..8ca24ebcb 100644 --- a/packages/bruno-electron/src/ipc/collection.js +++ b/packages/bruno-electron/src/ipc/collection.js @@ -22,7 +22,7 @@ const { safeToRename, isWindowsOS, isValidFilename, - hasSubFolders, + hasSubDirectories, } = require('../utils/filesystem'); const { openCollectionDialog } = require('../app/collections'); const { generateUidBasedOnHash, stringifyJson, safeParseJSON, safeStringifyJSON } = require('../utils/common'); @@ -345,6 +345,11 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection // rename item ipcMain.handle('renderer:rename-item', async (event, oldPath, newPath, newName) => { const tempDir = path.join(os.tmpdir(), `temp-folder-${Date.now()}`); + const parentDir = path.dirname(oldPath); + const isWindowsOSAndNotWSLAndItemHasSubDirectories = isWindowsOS() && !isWSLPath(oldPath) && hasSubDirectories(oldPath); + let parentDirUnwatched = false; + let parentDirRewatched = false; + try { // Normalize paths if they are WSL paths oldPath = isWSLPath(oldPath) ? normalizeWslPath(oldPath) : normalizeAndResolvePath(oldPath); @@ -367,10 +372,19 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection moveRequestUid(bruFile, newBruFilePath); } - watcher.unlink(path.dirname(oldPath)); + watcher.unlinkItemPathInWatcher(parentDir); + parentDirUnwatched = true; - if (isWindowsOS() && !isWSLPath(oldPath) && hasSubFolders(oldPath)) { - console.log('Windows OS: Moving folder with subfolders'); + /** + * If it is windows OS + * And it is not WSL path (meaning its not linux running on windows using WSL) + * And it has sub directories + * Only then we need to use the temp dir approach to rename the folder + * + * Windows OS would sometimes throw error when renaming a folder with sub directories + * This is a alternative approach to avoid that error + */ + if (isWindowsOSAndNotWSLAndItemHasSubDirectories) { await fsExtra.copy(oldPath, tempDir); await fsExtra.remove(oldPath); await fsExtra.move(tempDir, newPath, { overwrite: true }); @@ -378,7 +392,9 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection } else { await fs.renameSync(oldPath, newPath); } - watcher.add(path.dirname(newPath)); + watcher.addItemPathInWatcher(parentDir); + parentDirRewatched = true; + return newPath; } @@ -402,11 +418,15 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection return newPath; } catch (error) { - // add path back to watcher in case an error during the rename file operations - // adds unlinked path back to watcher if doesn't exist - watcher.add(path.dirname(oldPath)); - - if (isWindowsOS() && !isWSLPath(oldPath)) { + // in case an error occurs during the rename file operations after unlinking the parent dir + // and the rewatch fails, we need to add it back to watcher + if (parentDirUnwatched && !parentDirRewatched) { + watcher.addItemPathInWatcher(parentDir); + } + + // in case the rename file operations fails, and we see that the temp dir exists + // and the old path does not exist, we need to restore the data from the temp dir to the old path + if (isWindowsOSAndNotWSLAndItemHasSubDirectories) { if (fsExtra.pathExistsSync(tempDir) && !fsExtra.pathExistsSync(oldPath)) { try { await fsExtra.copy(tempDir, oldPath); @@ -416,6 +436,7 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection } } } + return Promise.reject(error); } }); diff --git a/packages/bruno-electron/src/utils/filesystem.js b/packages/bruno-electron/src/utils/filesystem.js index bd4e0aec0..ec393bd51 100644 --- a/packages/bruno-electron/src/utils/filesystem.js +++ b/packages/bruno-electron/src/utils/filesystem.js @@ -38,8 +38,7 @@ const isDirectory = (dirPath) => { } }; -const hasSubFolders = (dir) => { - console.log('Checking for subfolders in:', dir); +const hasSubDirectories = (dir) => { const files = fs.readdirSync(dir); return files.some(file => fs.statSync(path.join(dir, file)).isDirectory()); }; @@ -231,5 +230,5 @@ module.exports = { isWindowsOS, safeToRename, isValidFilename, - hasSubFolders + hasSubDirectories }; From 33e86a909768ed1b378edf4d26225d0263e0997e Mon Sep 17 00:00:00 2001 From: lohit Date: Sat, 14 Dec 2024 19:57:04 +0530 Subject: [PATCH 05/20] feat: replace nextjs with rsbuild (#3617) * poc: bruno app rsbuild * fix: updates --------- Co-authored-by: Anoop M D --- packages/bruno-app/.gitignore | 2 +- packages/bruno-app/next.config.js | 22 -------- packages/bruno-app/package.json | 27 +++++---- packages/bruno-app/rsbuild.config.mjs | 27 +++++++++ .../bruno-app/src/components/Portal/index.js | 6 +- .../components/RequestPane/QueryUrl/index.js | 2 +- .../Collection/CollectionItem/index.js | 2 +- .../bruno-app/src/components/Table/index.js | 10 ++-- packages/bruno-app/src/index.js | 14 +++++ .../bruno-app/src/pages/{_app.js => Main.js} | 56 +++++-------------- packages/bruno-app/src/pages/_document.js | 41 -------------- packages/bruno-app/src/pages/index.js | 16 ++---- .../src/providers/App/useTelemetry.js | 6 +- .../src/providers/ReduxStore/index.js | 4 +- .../ReduxStore/slices/notifications.js | 1 - packages/bruno-graphql-docs/package.json | 2 +- scripts/build-electron.sh | 4 +- 17 files changed, 91 insertions(+), 151 deletions(-) delete mode 100644 packages/bruno-app/next.config.js create mode 100644 packages/bruno-app/rsbuild.config.mjs create mode 100644 packages/bruno-app/src/index.js rename packages/bruno-app/src/pages/{_app.js => Main.js} (62%) delete mode 100644 packages/bruno-app/src/pages/_document.js diff --git a/packages/bruno-app/.gitignore b/packages/bruno-app/.gitignore index 26d2023a3..691ec3051 100644 --- a/packages/bruno-app/.gitignore +++ b/packages/bruno-app/.gitignore @@ -31,6 +31,6 @@ yarn-error.log* # next.js .next/ -out/ +dist/ .env \ No newline at end of file diff --git a/packages/bruno-app/next.config.js b/packages/bruno-app/next.config.js deleted file mode 100644 index 209858093..000000000 --- a/packages/bruno-app/next.config.js +++ /dev/null @@ -1,22 +0,0 @@ -module.exports = { - output: 'export', - reactStrictMode: false, - publicRuntimeConfig: { - CI: process.env.CI, - PLAYWRIGHT: process.env.PLAYWRIGHT, - ENV: process.env.ENV - }, - webpack: (config, { isServer }) => { - // Fixes npm packages that depend on `fs` module - if (!isServer) { - config.resolve.fallback.fs = false; - } - Object.defineProperty(config, 'devtool', { - get() { - return 'source-map'; - }, - set() {}, - }); - return config; - }, -}; diff --git a/packages/bruno-app/package.json b/packages/bruno-app/package.json index 52553b002..954e82b45 100644 --- a/packages/bruno-app/package.json +++ b/packages/bruno-app/package.json @@ -3,10 +3,9 @@ "version": "0.3.0", "private": true, "scripts": { - "dev": "cross-env ENV=dev next dev -p 3000", - "build": "next build", - "start": "next start", - "lint": "next lint", + "dev": "rsbuild dev --open", + "build": "rsbuild build -m production", + "preview": "rsbuild preview", "test": "jest", "test:prettier": "prettier --check \"./src/**/*.{js,jsx,json,ts,tsx}\"", "prettier": "prettier --write \"./src/**/*.{js,jsx,json,ts,tsx}\"" @@ -49,7 +48,6 @@ "markdown-it-replace-link": "^1.2.0", "mousetrap": "^1.6.5", "nanoid": "3.3.4", - "next": "14.2.16", "path": "^0.12.7", "pdfjs-dist": "4.4.168", "platform": "^1.3.6", @@ -57,17 +55,17 @@ "prettier": "^2.7.1", "qs": "^6.11.0", "query-string": "^7.0.1", - "react": "18.2.0", + "react": "19.0.0", "react-copy-to-clipboard": "^5.1.0", "react-dnd": "^16.0.1", "react-dnd-html5-backend": "^16.0.1", - "react-dom": "18.2.0", + "react-dom": "19.0.0", "react-hot-toast": "^2.4.0", "react-i18next": "^15.0.1", "react-inspector": "^6.0.2", "react-pdf": "9.1.1", "react-player": "^2.16.0", - "react-redux": "^7.2.6", + "react-redux": "^7.2.9", "react-tooltip": "^5.5.2", "sass": "^1.46.0", "strip-json-comments": "^5.0.1", @@ -79,13 +77,14 @@ "yup": "^0.32.11" }, "devDependencies": { - "@babel/core": "^7.16.0", - "@babel/plugin-transform-spread": "^7.16.7", - "@babel/preset-env": "^7.16.4", - "@babel/preset-react": "^7.16.0", - "@babel/runtime": "^7.16.3", + "@rsbuild/core": "^1.1.2", + "@rsbuild/plugin-babel": "^1.0.3", + "@rsbuild/plugin-node-polyfill": "^1.2.0", + "@rsbuild/plugin-react": "^1.0.7", + "@rsbuild/plugin-sass": "^1.1.0", + "@rsbuild/plugin-styled-components": "1.1.0", "autoprefixer": "10.4.20", - "babel-loader": "^8.2.3", + "babel-plugin-react-compiler": "19.0.0-beta-a7bf2bd-20241110", "cross-env": "^7.0.3", "css-loader": "7.1.2", "file-loader": "^6.2.0", diff --git a/packages/bruno-app/rsbuild.config.mjs b/packages/bruno-app/rsbuild.config.mjs new file mode 100644 index 000000000..704ea4d09 --- /dev/null +++ b/packages/bruno-app/rsbuild.config.mjs @@ -0,0 +1,27 @@ +import { defineConfig } from '@rsbuild/core'; +import { pluginReact } from '@rsbuild/plugin-react'; +import { pluginBabel } from '@rsbuild/plugin-babel'; +import { pluginStyledComponents } from '@rsbuild/plugin-styled-components'; +import { pluginSass } from '@rsbuild/plugin-sass'; +import { pluginNodePolyfill } from '@rsbuild/plugin-node-polyfill' + +export default defineConfig({ + plugins: [ + pluginNodePolyfill(), + pluginReact(), + pluginStyledComponents(), + pluginSass(), + pluginBabel({ + include: /\.(?:js|jsx|tsx)$/, + babelLoaderOptions(opts) { + opts.plugins?.unshift('babel-plugin-react-compiler'); + } + }) + ], + source: { + tsconfigPath: './jsconfig.json', // Specifies the path to the JavaScript/TypeScript configuration file + }, + html: { + title: 'Bruno' + }, +}); diff --git a/packages/bruno-app/src/components/Portal/index.js b/packages/bruno-app/src/components/Portal/index.js index 4d34c0a04..7007c4766 100644 --- a/packages/bruno-app/src/components/Portal/index.js +++ b/packages/bruno-app/src/components/Portal/index.js @@ -1,8 +1,6 @@ import { createPortal } from 'react-dom'; -function Portal({ children, wrapperId }) { - wrapperId = wrapperId || 'bruno-app-body'; - - return createPortal(children, document.getElementById(wrapperId)); +function Portal({ children }) { + return createPortal(children, document.body); } export default Portal; diff --git a/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js b/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js index f6d4ff308..9f3e600d0 100644 --- a/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js +++ b/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js @@ -70,7 +70,7 @@ const QueryUrl = ({ item, collection, handleRun }) => { const handleGenerateCode = (e) => { e.stopPropagation(); - if (item.request.url !== '' || (item.draft?.request.url !== undefined && item.draft?.request.url !== '')) { + if (item?.request?.url !== '' || (item.draft?.request?.url !== undefined && item.draft?.request?.url !== '')) { setGenerateCodeItemModalOpen(true); } else { toast.error('URL is required'); diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js index 09b9552b5..fb380c20e 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js @@ -183,7 +183,7 @@ const CollectionItem = ({ item, collection, searchText }) => { const handleGenerateCode = (e) => { e.stopPropagation(); dropdownTippyRef.current.hide(); - if (item.request.url !== '' || (item.draft?.request.url !== undefined && item.draft?.request.url !== '')) { + if (item?.request?.url !== '' || (item?.draft?.request?.url !== undefined && item?.draft?.request?.url !== '')) { setGenerateCodeItemModalOpen(true); } else { toast.error('URL is required'); diff --git a/packages/bruno-app/src/components/Table/index.js b/packages/bruno-app/src/components/Table/index.js index 80bfb19f3..7c9b48d7d 100644 --- a/packages/bruno-app/src/components/Table/index.js +++ b/packages/bruno-app/src/components/Table/index.js @@ -63,16 +63,16 @@ const Table = ({ minColumnWidth = 1, headers = [], children }) => { [activeColumnIndex, columns, minColumnWidth] ); - const handleMouseUp = useCallback(() => { - setActiveColumnIndex(null); - removeListeners(); - }, [removeListeners]); - const removeListeners = useCallback(() => { window.removeEventListener('mousemove', handleMouseMove); window.removeEventListener('mouseup', removeListeners); }, [handleMouseMove]); + const handleMouseUp = useCallback(() => { + setActiveColumnIndex(null); + removeListeners?.(); + }, [removeListeners]); + useEffect(() => { if (activeColumnIndex !== null) { window.addEventListener('mousemove', handleMouseMove); diff --git a/packages/bruno-app/src/index.js b/packages/bruno-app/src/index.js new file mode 100644 index 000000000..0e5187ebe --- /dev/null +++ b/packages/bruno-app/src/index.js @@ -0,0 +1,14 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './pages/index'; + +const rootElement = document.getElementById('root'); + +if (rootElement) { + const root = ReactDOM.createRoot(rootElement); + root.render( + + + + ); +} diff --git a/packages/bruno-app/src/pages/_app.js b/packages/bruno-app/src/pages/Main.js similarity index 62% rename from packages/bruno-app/src/pages/_app.js rename to packages/bruno-app/src/pages/Main.js index 08ebe6b0b..ba7b3289e 100644 --- a/packages/bruno-app/src/pages/_app.js +++ b/packages/bruno-app/src/pages/Main.js @@ -25,31 +25,7 @@ import '@fontsource/inter/900.css'; import { setupPolyfills } from 'utils/common/setupPolyfills'; setupPolyfills(); -function SafeHydrate({ children }) { - return
{typeof window === 'undefined' ? null : children}
; -} - -function NoSsr({ children }) { - const SERVER_RENDERED = typeof window === 'undefined'; - - if (SERVER_RENDERED) { - return null; - } - - return <>{children}; -} - -function MyApp({ Component, pageProps }) { - const [domLoaded, setDomLoaded] = useState(false); - - useEffect(() => { - setDomLoaded(true); - }, []); - - if (!domLoaded) { - return null; - } - +function Main({ children }) { if (!window.ipcRenderer) { return (