-
Notifications
You must be signed in to change notification settings - Fork 2k
/
Copy pathsections-middleware.js
130 lines (111 loc) · 4.56 KB
/
sections-middleware.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import config from '@automattic/calypso-config';
import page from '@automattic/calypso-router';
import * as LoadingError from 'calypso/layout/error';
import { performanceTrackerStart } from 'calypso/lib/performance-tracking';
import { bumpStat } from 'calypso/state/analytics/actions';
import { setSectionLoading } from 'calypso/state/ui/actions';
import { activateNextLayoutFocus } from 'calypso/state/ui/layout-focus/actions';
import * as controller from './controller/index.web';
import { composeHandlers } from './controller/shared';
import sections from './sections';
import isSectionEnabled from './sections-filter';
import { receiveSections, load } from './sections-helper';
import { pathToRegExp } from './utils';
receiveSections( sections );
function activateSection( section, context ) {
controller.setSectionMiddleware( section )( context );
context.store.dispatch( activateNextLayoutFocus() );
}
async function loadSection( context, sectionDefinition ) {
context.store.dispatch( setSectionLoading( true ) );
// If the section chunk is not loaded within 400ms, report it to analytics
const loadReportTimeout = setTimeout( () => {
context.store.dispatch( bumpStat( 'calypso_chunk_waiting', sectionDefinition.name ) );
}, 400 );
try {
// load the section module, i.e., its webpack chunk
const requiredModule = await load( sectionDefinition.name, sectionDefinition.module );
// call the module initialization function (possibly async, registers page.js handlers etc.)
await requiredModule.default( controller.clientRouter );
} finally {
context.store.dispatch( setSectionLoading( false ) );
// If the load was faster than the timeout, this will cancel the analytics reporting
clearTimeout( loadReportTimeout );
}
}
/**
* Cache of already loaded or loading section modules. Every section module is in one of
* three states regarding the cache:
* - no record in the cache: not loaded or not currently loading
* - record value is `true`: already loaded and initialized
* - record value is a `Promise`: is currently loading, the promise will fulfill when done.
* Don't start a second load with `loadSection` but rather wait for the existing promise.
*/
const _loadedSections = {};
function loadSectionHandler( sectionDefinition ) {
return async ( context, next ) => {
try {
const loadedSection = _loadedSections[ sectionDefinition.module ];
if ( loadedSection ) {
// wait for the promise if loading, do nothing when already loaded
if ( loadedSection !== true ) {
await loadedSection;
}
} else {
// start loading the section and record the `Promise` in a map
const loadingSection = loadSection( context, sectionDefinition );
_loadedSections[ sectionDefinition.module ] = loadingSection;
// wait until the section module is loaded and the set the map record to `true`
await loadingSection;
_loadedSections[ sectionDefinition.module ] = true;
}
// activate the section after ensuring it's fully loaded
activateSection( sectionDefinition, context );
next();
} catch ( error ) {
// delete the cache record on failure; next attempt to load will start from scratch
delete _loadedSections[ sectionDefinition.module ];
console.error( error ); // eslint-disable-line
if ( ! LoadingError.isRetry() && process.env.NODE_ENV !== 'development' ) {
LoadingError.retry( sectionDefinition.name );
} else {
LoadingError.show( context, sectionDefinition.name );
}
}
};
}
function createPageDefinition( path, sectionDefinition ) {
// skip this section if it's not enabled in current environment
const { envId } = sectionDefinition;
if ( envId && ! envId.includes( config( 'env_id' ) ) ) {
return;
}
const pathRegex = pathToRegExp( path );
let handler = loadSectionHandler( sectionDefinition );
// Install navigation performance tracking.
if ( sectionDefinition.trackLoadPerformance ) {
handler = composeHandlers( performanceTrackerStart( sectionDefinition.name ), handler );
}
// if the section doesn't support logged-out views, redirect to login if user is not logged in
if ( ! sectionDefinition.enableLoggedOut ) {
handler = composeHandlers( controller.redirectLoggedOut, handler );
}
page( pathRegex, handler );
}
export const setupRoutes = () => {
for ( const section of sections ) {
if ( ! isSectionEnabled( section ) ) {
continue;
}
for ( const path of section.paths ) {
createPageDefinition( path, section );
}
}
};
if ( module.hot ) {
module.hot.accept( './sections', () => {
const updatedSections = require( './sections' ).default;
receiveSections( updatedSections );
setupRoutes();
} );
}