From 14a618f374893ee578817775e0dba5268f3c6cb9 Mon Sep 17 00:00:00 2001 From: Nolan Lawson Date: Sun, 16 Dec 2018 10:22:34 -0800 Subject: [PATCH] perf: lazy-load logged-in observers, fix circular dependencies (#823) * perf: lazy-load logged-in observers, fix circular dependencies * I guess async deps don't count as circular deps --- package-lock.json | 33 +++++++------------ package.json | 1 + .../_store/observers/autosuggestObservers.js | 3 +- .../observers/customScrollbarObservers.js | 4 ++- .../_store/observers/instanceObservers.js | 3 +- .../_store/observers/loggedInObservers.js | 17 ++++++++++ .../_store/observers/notificationObservers.js | 3 +- .../notificationPermissionObservers.js | 4 ++- src/routes/_store/observers/observers.js | 14 ++------ .../observers/setupLoggedInObservers.js | 15 +++++++++ .../_store/observers/timelineObservers.js | 3 +- src/routes/_utils/asyncModules.js | 4 +++ webpack/client.config.js | 6 ++++ 13 files changed, 71 insertions(+), 39 deletions(-) create mode 100644 src/routes/_store/observers/loggedInObservers.js create mode 100644 src/routes/_store/observers/setupLoggedInObservers.js diff --git a/package-lock.json b/package-lock.json index 724d62ee..6d408df9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2061,14 +2061,12 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "optional": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2083,20 +2081,17 @@ "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "optional": true + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "optional": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "optional": true + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, "core-util-is": { "version": "1.0.2", @@ -2213,8 +2208,7 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "optional": true + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "ini": { "version": "1.3.5", @@ -2226,7 +2220,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -2241,7 +2234,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -2249,14 +2241,12 @@ "minimist": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "optional": true + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "minipass": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.2.4.tgz", "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==", - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -2275,7 +2265,6 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "optional": true, "requires": { "minimist": "0.0.8" } @@ -2356,8 +2345,7 @@ "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "optional": true + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, "object-assign": { "version": "4.1.1", @@ -2369,7 +2357,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "optional": true, "requires": { "wrappy": "1" } @@ -2491,7 +2478,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -2634,6 +2620,11 @@ "safe-buffer": "^5.0.1" } }, + "circular-dependency-plugin": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.0.2.tgz", + "integrity": "sha512-oC7/DVAyfcY3UWKm0sN/oVoDedQDQiw/vIiAnuTWTpE5s0zWf7l3WY417Xw/Fbi/QbAjctAkxgMiS9P0s3zkmA==" + }, "circular-json": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", diff --git a/package.json b/package.json index e41b74d5..aeff1eec 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "cheerio": "^1.0.0-rc.2", "child-process-promise": "^2.2.1", "chokidar": "^2.0.4", + "circular-dependency-plugin": "^5.0.2", "compression": "^1.7.3", "cross-env": "^5.2.0", "css-loader": "^2.0.1", diff --git a/src/routes/_store/observers/autosuggestObservers.js b/src/routes/_store/observers/autosuggestObservers.js index c213c219..19fc01ec 100644 --- a/src/routes/_store/observers/autosuggestObservers.js +++ b/src/routes/_store/observers/autosuggestObservers.js @@ -1,4 +1,5 @@ import { database } from '../../_database/database' +import { store } from '../store' const SEARCH_RESULTS_LIMIT = 4 const DATABASE_SEARCH_RESULTS_LIMIT = 30 @@ -20,7 +21,7 @@ function searchEmoji (store, searchText) { return results } -export function autosuggestObservers (store) { +export function autosuggestObservers () { store.observe('autosuggestSearchText', async autosuggestSearchText => { let { composeFocused } = store.get() if (!composeFocused || !autosuggestSearchText) { diff --git a/src/routes/_store/observers/customScrollbarObservers.js b/src/routes/_store/observers/customScrollbarObservers.js index 231b621b..0a038669 100644 --- a/src/routes/_store/observers/customScrollbarObservers.js +++ b/src/routes/_store/observers/customScrollbarObservers.js @@ -1,6 +1,8 @@ +import { store } from '../store' + let theScrollbarStyle = process.browser && document.getElementById('theScrollbarStyle') -export function customScrollbarObservers (store) { +export function customScrollbarObservers () { store.observe('disableCustomScrollbars', disableCustomScrollbars => { if (!process.browser) { return diff --git a/src/routes/_store/observers/instanceObservers.js b/src/routes/_store/observers/instanceObservers.js index 1e63f409..6399ceef 100644 --- a/src/routes/_store/observers/instanceObservers.js +++ b/src/routes/_store/observers/instanceObservers.js @@ -8,6 +8,7 @@ import { getTimeline } from '../../_api/timelines' import { TIMELINE_BATCH_SIZE } from '../../_static/timelines' import { scheduleIdleTask } from '../../_utils/scheduleIdleTask' import { mark, stop } from '../../_utils/marks' +import { store } from '../store' // stream to watch for home timeline updates and notifications let currentInstanceStream @@ -92,7 +93,7 @@ async function fillGap (instanceName, accessToken, timelineName, firstTimelineIt } } -export function instanceObservers (store) { +export function instanceObservers () { store.observe('currentInstance', async (currentInstance) => { if (!process.browser) { return diff --git a/src/routes/_store/observers/loggedInObservers.js b/src/routes/_store/observers/loggedInObservers.js new file mode 100644 index 00000000..35261f59 --- /dev/null +++ b/src/routes/_store/observers/loggedInObservers.js @@ -0,0 +1,17 @@ +import { instanceObservers } from './instanceObservers' +import { timelineObservers } from './timelineObservers' +import { notificationObservers } from './notificationObservers' +import { autosuggestObservers } from './autosuggestObservers' +import { notificationPermissionObservers } from './notificationPermissionObservers' +import { customScrollbarObservers } from './customScrollbarObservers' + +// These observers can be lazy-loaded when the user is actually logged in. +// Prevents circular dependencies and reduces the size of main.js +export default function loggedInObservers () { + instanceObservers() + timelineObservers() + notificationObservers() + autosuggestObservers() + notificationPermissionObservers() + customScrollbarObservers() +} diff --git a/src/routes/_store/observers/notificationObservers.js b/src/routes/_store/observers/notificationObservers.js index 790d37a4..1b70bb6d 100644 --- a/src/routes/_store/observers/notificationObservers.js +++ b/src/routes/_store/observers/notificationObservers.js @@ -1,9 +1,10 @@ import { setFavicon } from '../../_utils/setFavicon' import { runMediumPriorityTask } from '../../_utils/runMediumPriorityTask' +import { store } from '../store' let currentFaviconHasNotifications = false -export function notificationObservers (store) { +export function notificationObservers () { store.observe('hasNotifications', hasNotifications => { if (!process.browser) { return diff --git a/src/routes/_store/observers/notificationPermissionObservers.js b/src/routes/_store/observers/notificationPermissionObservers.js index 26b0e40f..7ad9595a 100644 --- a/src/routes/_store/observers/notificationPermissionObservers.js +++ b/src/routes/_store/observers/notificationPermissionObservers.js @@ -1,4 +1,6 @@ -export function notificationPermissionObservers (store) { +import { store } from '../store' + +export function notificationPermissionObservers () { if (!process.browser || !navigator.permissions || !navigator.permissions.query) { return } diff --git a/src/routes/_store/observers/observers.js b/src/routes/_store/observers/observers.js index 278424d4..4c5360ae 100644 --- a/src/routes/_store/observers/observers.js +++ b/src/routes/_store/observers/observers.js @@ -1,23 +1,13 @@ -import { instanceObservers } from './instanceObservers' -import { timelineObservers } from './timelineObservers' -import { notificationObservers } from './notificationObservers' import { onlineObservers } from './onlineObservers' import { navObservers } from './navObservers' -import { autosuggestObservers } from './autosuggestObservers' import { pageVisibilityObservers } from './pageVisibilityObservers' import { resizeObservers } from './resizeObservers' -import { notificationPermissionObservers } from './notificationPermissionObservers' -import { customScrollbarObservers } from './customScrollbarObservers' +import { setupLoggedInObservers } from './setupLoggedInObservers' export function observers (store) { - instanceObservers(store) - timelineObservers(store) - notificationObservers(store) onlineObservers(store) navObservers(store) - autosuggestObservers(store) pageVisibilityObservers(store) resizeObservers(store) - notificationPermissionObservers(store) - customScrollbarObservers(store) + setupLoggedInObservers(store) } diff --git a/src/routes/_store/observers/setupLoggedInObservers.js b/src/routes/_store/observers/setupLoggedInObservers.js new file mode 100644 index 00000000..614635a5 --- /dev/null +++ b/src/routes/_store/observers/setupLoggedInObservers.js @@ -0,0 +1,15 @@ +import { importLoggedInObservers } from '../../_utils/asyncModules' + +let observedOnce = false + +// An observer that calls an observer... this is a bit weird, but it eliminates +// circular dependencies and also allows us to lazy load observers that are +// only needed when you're logged in. +export function setupLoggedInObservers (store) { + store.observe('isUserLoggedIn', isUserLoggedIn => { + if (isUserLoggedIn && !observedOnce) { + importLoggedInObservers().then(loggedInObservers => loggedInObservers()) + observedOnce = true + } + }) +} diff --git a/src/routes/_store/observers/timelineObservers.js b/src/routes/_store/observers/timelineObservers.js index 644c9c63..cb5885a4 100644 --- a/src/routes/_store/observers/timelineObservers.js +++ b/src/routes/_store/observers/timelineObservers.js @@ -3,8 +3,9 @@ import { createStream } from '../../_actions/streaming' import { getTimeline } from '../../_api/timelines' import { addStatusesOrNotifications } from '../../_actions/addStatusOrNotification' import { TIMELINE_BATCH_SIZE } from '../../_static/timelines' +import { store } from '../store' -export function timelineObservers (store) { +export function timelineObservers () { // stream to watch for local/federated/etc. updates. home and notification // updates are handled in timelineObservers.js let currentTimelineStream diff --git a/src/routes/_utils/asyncModules.js b/src/routes/_utils/asyncModules.js index c067f2af..a826b9a0 100644 --- a/src/routes/_utils/asyncModules.js +++ b/src/routes/_utils/asyncModules.js @@ -31,3 +31,7 @@ export const importNotificationVirtualListItem = () => import( export const importDatabase = () => import( /* webpackChunkName: 'database.js' */ '../_database/databaseApis.js' ) + +export const importLoggedInObservers = () => import( + /* webpackChunkName: 'loggedInObservers.js' */ '../_store/observers/loggedInObservers.js' + ).then(getDefault) diff --git a/webpack/client.config.js b/webpack/client.config.js index 27f05284..99042fbc 100644 --- a/webpack/client.config.js +++ b/webpack/client.config.js @@ -3,6 +3,7 @@ const config = require('sapper/config/webpack.js') const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin const LodashModuleReplacementPlugin = require('lodash-webpack-plugin') const terser = require('./terser.config') +const CircularDependencyPlugin = require('circular-dependency-plugin') const { mode, dev } = require('./shared.config') module.exports = { @@ -50,6 +51,11 @@ module.exports = { ), new LodashModuleReplacementPlugin({ paths: true + }), + new CircularDependencyPlugin({ + exclude: /node_modules/, + failOnError: true, + cwd: process.cwd() }) ].concat(dev ? [ new webpack.HotModuleReplacementPlugin({