diff --git a/src/dashboard/Dashboard.js b/src/dashboard/Dashboard.js
index c7c88da08..f5f43b014 100644
--- a/src/dashboard/Dashboard.js
+++ b/src/dashboard/Dashboard.js
@@ -12,12 +12,12 @@ import AppData from './AppData.react';
import AppsIndex from './Apps/AppsIndex.react';
import AppsManager from 'lib/AppsManager';
import Browser from './Data/Browser/Browser.react';
-import CloudCode from './Data/CloudCode/B4ACloudCode.react';
+// import CloudCode from './Data/CloudCode/B4ACloudCode.react';
import AppOverview from './Data/AppOverview/AppOverview.react';
import Config from './Data/Config/Config.react';
import FourOhFour from 'components/FourOhFour/FourOhFour.react';
import GeneralSettings from './Settings/GeneralSettings.react';
-import GraphQLConsole from './Data/ApiConsole/GraphQLConsole.react';
+// import GraphQLConsole from './Data/ApiConsole/GraphQLConsole.react';
// import HostingSettings from './Settings/HostingSettings.react';
import HubConnections from './Hub/HubConnections.react';
import IndexManager from './IndexManager/IndexManager.react'
@@ -36,7 +36,7 @@ import { get } from 'lib/AJAX';
import { setBasePath } from 'lib/AJAX';
import ServerSettings from 'dashboard/ServerSettings/ServerSettings.react';
import { Helmet } from 'react-helmet';
-import Playground from './Data/Playground/Playground.react';
+// import Playground from './Data/Playground/Playground.react';
import axios from 'lib/axios';
// import moment from 'moment';
import B4aConnectPage from './B4aConnectPage/B4aConnectPage.react';
@@ -49,7 +49,7 @@ import PushDetails from './Push/PushDetails.react';
import PushIndex from './Push/PushIndex.react';
import PushNew from './Push/PushNew.react';
// import PushSettings from './Settings/PushSettings.react';
-import React, { useCallback, useEffect, useMemo, useState } from 'react';
+import React, { useCallback, useEffect, useMemo, useState, Suspense, lazy } from 'react';
import RestConsole from './Data/ApiConsole/RestConsole.react';
// import SchemaOverview from './Data/Browser/SchemaOverview.react';
import SecuritySettings from './Settings/SecuritySettings.react';
@@ -66,6 +66,10 @@ import back4app2 from '../lib/back4app2';
import { initializeAmplitude } from 'lib/amplitudeEvents';
import { setUser as setSentryUser } from '@sentry/react';
+const LazyGraphQLConsole = lazy(() => import('./Data/ApiConsole/GraphQLConsole.react'));
+const LazyPlayground = lazy(() => import('./Data/Playground/Playground.react'));
+const LazyCloudCode = lazy(() => import('./Data/CloudCode/B4ACloudCode.react'));
+
const ShowSchemaOverview = false; //In progress features. Change false to true to work on this feature.
// class Empty extends React.Component {
@@ -143,6 +147,44 @@ const waitForScriptToLoad = async conditionFn => {
throw new Error('Script not loaded yet!');
};
+const preloadMap = {
+ cloudCode: () => import('./Data/CloudCode/B4ACloudCode.react'),
+ graphqlConsole: () => import('./Data/ApiConsole/GraphQLConsole.react'),
+ playground: () => import('./Data/Playground/Playground.react'),
+};
+
+// Preload all routes with proper error handling and logging
+const preloadRoute = async (routeName, preloadFn) => {
+ try {
+ await preloadFn();
+ console.log(`Successfully preloaded route: ${routeName}`);
+ } catch (err) {
+ console.error(`Error preloading route ${routeName}:`, err);
+ }
+};
+
+// Preload all routes in parallel
+const preloadAllRoutes = () => {
+ console.log('Preloading routes...');
+ return Promise.all(
+ Object.entries(preloadMap).map(([routeName, preloadFn]) =>
+ preloadRoute(routeName, preloadFn)
+ )
+ );
+};
+
+const LoadingComponent = () => (
+
+
+
+);
+
+const LazyComponentWrapper = ({ children }) => (
+ }>
+ {children}
+
+);
+
class Dashboard extends React.Component {
constructor(props) {
super();
@@ -160,6 +202,11 @@ class Dashboard extends React.Component {
}
componentDidMount() {
+ // Start preloading routes immediately but don't block on it
+ preloadAllRoutes().finally(() => {
+ console.log('Route preloading complete');
+ });
+
get('/parse-dashboard-config.json').then(({ apps, newFeaturesInLatestVersion = [], user }) => {
fetchHubUser().then(userDetail => {
user.createdAt = userDetail.createdAt;
@@ -383,8 +430,8 @@ class Dashboard extends React.Component {
const ApiConsoleRoute = (
}>
} />
- } />
- } />
+ } />
+ } />
} />
);
@@ -400,7 +447,7 @@ class Dashboard extends React.Component {
} />
} />
- } />
+ } />
} />
{JobsRoute}
diff --git a/src/dashboard/Data/AppOverview/AppOverview.react.js b/src/dashboard/Data/AppOverview/AppOverview.react.js
index fd479b2eb..c52482c82 100644
--- a/src/dashboard/Data/AppOverview/AppOverview.react.js
+++ b/src/dashboard/Data/AppOverview/AppOverview.react.js
@@ -6,7 +6,7 @@
* This source code is licensed under the license found in the LICENSE file in
* the root directory of this source tree.
*/
-import React from 'react';
+import React, { Suspense, lazy } from 'react';
import DashboardView from 'dashboard/DashboardView.react';
import styles from 'dashboard/Data/AppOverview/AppOverview.scss';
import { withRouter } from 'lib/withRouter';
@@ -18,11 +18,12 @@ import AppSecurityCard from './AppSecurityCard.react';
import AppPerformanceCard from './AppPerformanceCard.react';
import AppLoadingText from './AppLoadingText.react';
import B4aTooltip from 'components/Tooltip/B4aTooltip.react';
-import ConnectAppModal from './ConnectAppModal.react';
+// import ConnectAppModal from './ConnectAppModal.react';
import OnboardingBoxes from './OnboardingBoxes.react';
import AccountManager from 'lib/AccountManager';
import { amplitudeLogEvent } from 'lib/amplitudeEvents';
+const LazyConnectAppModal = lazy(() => import('./ConnectAppModal.react'));
@withRouter
class AppOverview extends DashboardView {
constructor() {
@@ -73,6 +74,10 @@ class AppOverview extends DashboardView {
this.loadCardInformation();
}
+ componentDidMount() {
+ import('./ConnectAppModal.react');
+ }
+
copyText(copyText = '') {
if (navigator) {
navigator.clipboard.writeText(copyText);
@@ -300,7 +305,9 @@ class AppOverview extends DashboardView {
{this.state.showConnectAppModal && (
- this.setState({ showConnectAppModal: false })} />
+
+ this.setState({ showConnectAppModal: false })} />
+
)}
);
diff --git a/src/dashboard/index.js b/src/dashboard/index.js
index 383973cae..c35bb04e5 100644
--- a/src/dashboard/index.js
+++ b/src/dashboard/index.js
@@ -9,14 +9,18 @@ import './instrument';
import 'core-js/stable';
import 'regenerator-runtime/runtime';
import Immutable from 'immutable';
-import installDevTools from 'immutable-devtools';
+// import installDevTools from 'immutable-devtools';
import React from 'react';
import ReactDOM from 'react-dom';
import Dashboard from './Dashboard';
require('stylesheets/fonts.scss');
require('graphiql/graphiql.min.css');
-installDevTools(Immutable);
+
+if (process.env.NODE_ENV !== 'production') {
+ const installDevTools = require('immutable-devtools');
+ installDevTools(Immutable);
+}
const path = window.PARSE_DASHBOARD_PATH || '/';
ReactDOM.render(, document.getElementById('browser_mount'));
diff --git a/webpack/homolog.config.js b/webpack/homolog.config.js
index febe18273..1da74ac75 100644
--- a/webpack/homolog.config.js
+++ b/webpack/homolog.config.js
@@ -1,41 +1,69 @@
-const configuration = require('./publish.config.js');
+/*
+ * Copyright (c) 2016-present, Parse, LLC
+ * All rights reserved.
+ *
+ * This source code is licensed under the license found in the LICENSE file in
+ * the root directory of this source tree.
+ */
+const configuration = require('./base.config.js');
const webpack = require('webpack');
-const TerserPlugin = require('terser-webpack-plugin');
+const path = require('path');
+const HtmlWebpackPlugin = require('html-webpack-plugin');
+// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
+
+configuration.mode = 'production';
+configuration.entry = {
+ dashboard: './dashboard/index.js',
+};
+
+configuration.output.path = path.resolve('./Parse-Dashboard/public/bundles');
+configuration.output.filename = '[name].[chunkhash].js';
+
+configuration.optimization = {
+ usedExports: true,
+ minimize: true,
+ concatenateModules: true,
+ sideEffects: true,
+ providedExports: true,
+ innerGraph: true,
+ splitChunks: {
+ chunks: 'all',
+ minSize: 20000,
+ maxAsyncRequests: 30,
+ maxInitialRequests: 30
+ }
+};
configuration.plugins.push(
+ new HtmlWebpackPlugin({
+ template: '../Parse-Dashboard/index.ejs',
+ filename: path.resolve('./Parse-Dashboard/public/index.html')
+ }),
new webpack.DefinePlugin({
'process.env': {
- 'NODE_ENV': JSON.stringify('homolog'),
- 'SENTRY_ENV': JSON.stringify('homolog')
+ 'NODE_ENV': JSON.stringify('production'),
+ 'SENTRY_ENV': JSON.stringify('production')
}
}),
- new webpack.SourceMapDevToolPlugin({
- filename: '[file].map',
- include: /dashboard.*.*/
- })
+ // new BundleAnalyzerPlugin()
);
-configuration.optimization = {
- splitChunks: {
- cacheGroups: {
- commons: {
- test: /[\\/]node_modules[\\/]/,
- name: 'vendors',
- chunks: 'all'
- }
+configuration.module = configuration.module || {};
+configuration.module.rules = configuration.module.rules || [];
+
+// Ensure babel-loader is configured for tree shaking
+const babelRule = configuration.module.rules.find(rule => rule.use && rule.use.includes('babel-loader'));
+if (babelRule) {
+ babelRule.use = {
+ loader: 'babel-loader',
+ options: {
+ presets: [
+ ['@babel/preset-env', {
+ modules: false // This is important for tree shaking
+ }]
+ ]
}
- },
- minimize: true,
- minimizer: [
- new TerserPlugin({
- // cache: true,
- parallel: true,
- terserOptions: {
- sourceMap: true,
- mangle: false
- }
- })
- ]
+ };
}
-module.exports = configuration;
\ No newline at end of file
+module.exports = configuration;