@@ -18,60 +18,95 @@ import tasks from '../tasks/router/tasks.router';
1818import billing , { organizationRoutes as billingOrganizationRoutes } from '../billing/router/billing.router' ;
1919import legal from '../legal/router/legal.router' ;
2020
21- // Core modules — always mounted
22- const coreRoutes = [ ] . concat ( home , auth , users ) ;
23-
2421/**
25- * Admin child modules — routes injected as children of the `/admin` parent
26- * route via `injectAdminChildren`. Downstream projects should register any
27- * module that contributes an admin tab here (see MIGRATIONS.md).
28- *
29- * Each module's router file should export routes with **relative** paths
30- * (e.g. `'my-tab'` rather than `'/admin/my-tab'`) so they resolve under
31- * the `/admin/` parent.
22+ * Downstream route registries — mutated by `registerDownstreamRoutes` before
23+ * the router is instantiated. Module-load order guarantees downstream code
24+ * (imported before this file's composition runs) populates these arrays first.
3225 *
33- * @example
34- * import myTabRoutes from '../my-tab/router/my-tab.router';
35- * const adminChildModules = [
36- * { name: 'my-tab', routes: myTabRoutes },
37- * ];
26+ * @type {Array }
3827 */
39- const adminChildModules = [ ] ;
40- injectAdminChildren ( admin , adminChildModules , isModuleActive ) ;
28+ const _downstreamCoreModules = [ ] ;
29+ const _downstreamAdminChildModules = [ ] ;
30+ const _downstreamOptionalModules = [ ] ;
4131
4232/**
43- * Organization-settings child modules — routes injected as children of the
44- * `/users/organizations/:organizationId` parent route via `injectModuleChildren`.
45- * Base devkit ships this empty; PR (c) and downstream projects populate it
46- * (e.g. a billing-settings tab rendered inside the org detail layout).
33+ * Register downstream-specific route extensions.
34+ *
35+ * Call this from your downstream module (e.g. `src/modules/<project>/index.js`)
36+ * BEFORE the router is instantiated. Calling it mutates the internal registry
37+ * arrays in place; the router composition picks up the additions automatically.
4738 *
48- * Each module's router file should export routes with **relative** paths
49- * (e.g. `'billing'` rather than `'/users/organizations/:organizationId/billing'`)
50- * so they resolve under the org parent.
39+ * @param {object } [options={}]
40+ * @param {Array } [options.coreModules] Routes spread into `coreRoutes` (always mounted, no activation gate).
41+ * @param {Array } [options.adminChildModules] Added to `adminChildModules` (injected under `/admin`).
42+ * @param {Array } [options.optionalModules] Added to `optionalModules` (gated by `isModuleActive`).
43+ * @returns {void }
5144 */
52- const organizationChildModules = [
53- { name : 'billing' , routes : billingOrganizationRoutes } ,
54- ] ;
55- injectModuleChildren ( organizations , organizationChildModules , isModuleActive , ORG_PARENT_PATH ) ;
56-
57- // Optional modules — mounted only when activated
58- const optionalModules = [
59- { name : 'organizations' , routes : organizations } ,
60- { name : 'admin' , routes : admin } ,
61- { name : 'tasks' , routes : tasks } ,
62- { name : 'billing' , routes : billing } ,
63- { name : 'legal' , routes : legal } ,
64- ] ;
65-
66- const routes = optionalModules . reduce (
67- ( acc , mod ) => ( isModuleActive ( mod . name ) ? acc . concat ( mod . routes ) : acc ) ,
68- coreRoutes ,
69- ) ;
45+ export function registerDownstreamRoutes ( options = { } ) {
46+ if ( options . coreModules ) _downstreamCoreModules . push ( ...options . coreModules ) ;
47+ if ( options . adminChildModules ) _downstreamAdminChildModules . push ( ...options . adminChildModules ) ;
48+ if ( options . optionalModules ) _downstreamOptionalModules . push ( ...options . optionalModules ) ;
49+ }
7050
7151/**
7252 * Router configuration.
53+ *
54+ * Route composition is deferred inside `getRouter()` so that
55+ * `registerDownstreamRoutes` calls made during module initialisation (before
56+ * `getRouter` is invoked from `main.js`) are always visible to the composition.
7357 */
7458const getRouter = ( ) => {
59+ // Core modules — always mounted
60+ const coreRoutes = [ ] . concat ( home , auth , users , ..._downstreamCoreModules ) ;
61+
62+ /**
63+ * Admin child modules — routes injected as children of the `/admin` parent
64+ * route via `injectAdminChildren`. Downstream projects should register any
65+ * module that contributes an admin tab here (see MIGRATIONS.md).
66+ *
67+ * Each module's router file should export routes with **relative** paths
68+ * (e.g. `'my-tab'` rather than `'/admin/my-tab'`) so they resolve under
69+ * the `/admin/` parent.
70+ *
71+ * @example
72+ * import myTabRoutes from '../my-tab/router/my-tab.router';
73+ * const adminChildModules = [
74+ * { name: 'my-tab', routes: myTabRoutes },
75+ * ];
76+ */
77+ const adminChildModules = [ ..._downstreamAdminChildModules ] ;
78+ injectAdminChildren ( admin , adminChildModules , isModuleActive ) ;
79+
80+ /**
81+ * Organization-settings child modules — routes injected as children of the
82+ * `/users/organizations/:organizationId` parent route via `injectModuleChildren`.
83+ * Base devkit ships this empty; PR (c) and downstream projects populate it
84+ * (e.g. a billing-settings tab rendered inside the org detail layout).
85+ *
86+ * Each module's router file should export routes with **relative** paths
87+ * (e.g. `'billing'` rather than `'/users/organizations/:organizationId/billing'`)
88+ * so they resolve under the org parent.
89+ */
90+ const organizationChildModules = [
91+ { name : 'billing' , routes : billingOrganizationRoutes } ,
92+ ] ;
93+ injectModuleChildren ( organizations , organizationChildModules , isModuleActive , ORG_PARENT_PATH ) ;
94+
95+ // Optional modules — mounted only when activated
96+ const optionalModules = [
97+ { name : 'organizations' , routes : organizations } ,
98+ { name : 'admin' , routes : admin } ,
99+ { name : 'tasks' , routes : tasks } ,
100+ { name : 'billing' , routes : billing } ,
101+ { name : 'legal' , routes : legal } ,
102+ ..._downstreamOptionalModules ,
103+ ] ;
104+
105+ const routes = optionalModules . reduce (
106+ ( acc , mod ) => ( isModuleActive ( mod . name ) ? acc . concat ( mod . routes ) : acc ) ,
107+ coreRoutes ,
108+ ) ;
109+
75110 const router = createRouter ( {
76111 history : createWebHistory ( import . meta. env . BASE_URL ) ,
77112 routes,
0 commit comments