Versions: @abp/ng.cms-kit + @volo/abp.ng.cms-kit-pro 10.4.1, Angular 21, esbuild/@angular/build.
Problem: provideCmsKitAdminConfig() / provideCmsKitProAdminConfig() (and eCmsKit*AdminRouteNames) live in …/admin/config, whose fesm has a top-level import { CmsSettingsComponent } from '…/admin'. ./admin is one monolithic, side-effectful fesm (statically imports ToastuiEditorComponent + CodeMirrorEditorComponent + all grids). Because the import is side-effectful, bundlers cannot tree-shake it, so registering the CMS menu eagerly forces ~1.1 MB of admin/editor code into the initial bundle, even for apps that lazy-load the /cms routes and rarely use the editor.
Request: Split a component-free menu/route-metadata registration (the RoutesService.add(...) calls + the route-name/policy enums) into its own entry point (e.g. …/admin/config/routes) that does not import CmsSettingsComponent/./admin. Then apps can register the menu eagerly and lazy-load the heavy admin (incl. the settings tab via a lazily-loaded component) without pulling the editor into the initial bundle.
3 Answer(s)
-
0
Hi,
You're right —
provideCmsKitAdminConfig()andprovideCmsKitProAdminConfig()both drag the heavy admin entry into the initial bundle, exactly as you described. The chain runs throughsetting-tab.provider.ts, which top-level importsCmsSettingsComponentfrom@abp/ng.cms-kit/admin/@volo/abp.ng.cms-kit-pro/admin. Those entriesexport *from./components, and a few of those components (page-form,blog-post-form,global-resources, and Pro'sfaq-question-modal) statically importToastuiEditorComponentandCodeMirrorEditorComponent. So registering the menu eagerly is enough to anchor the editor + grids in the initial graph, even when the actual/cmsroutes are lazy-loaded.We'll track this and move the menu/route registration out of the heavy entry in a future release — the model
@volo/abp.ng.identity/configalready uses, whereIdentitySettingsComponentlives inside the config entry itself.In the meantime, drop this file into your app and call these in place of
provideCmsKitAdminConfig()/provideCmsKitProAdminConfig():import { eLayoutType, RoutesService } from '@abp/ng.core'; import { EnvironmentProviders, inject, makeEnvironmentProviders, provideAppInitializer, } from '@angular/core'; // Enum values inlined from @abp/ng.cms-kit/admin/config and // @volo/abp.ng.cms-kit-pro/admin/config. Importing any symbol from those // entries pulls setting-tab.provider.ts in, which top-level imports // CmsSettingsComponent from the heavy admin entry — that's the import that // drags the editor + grids into the initial bundle. const CmsKitRouteNames = { Cms: 'CmsKit::Menu:CMS', BlogPosts: 'CmsKit::BlogPosts', Blogs: 'CmsKit::Blogs', Comments: 'CmsKit::CmsKit.Comments', GlobalResources: 'CmsKit::GlobalResources', Menus: 'CmsKit::Menus', Pages: 'CmsKit::Pages', Tags: 'CmsKit::CmsKit.Tags', } as const; const CmsKitPolicyNames = { BlogPosts: 'CmsKit.BlogPosts', Blogs: 'CmsKit.Blogs', Comments: 'CmsKit.Comments', GlobalResources: 'CmsKit.GlobalResources', Menus: 'CmsKit.Menus', Pages: 'CmsKit.Pages', Tags: 'CmsKit.Tags', } as const; const CmsKitProRouteNames = { Cms: 'CmsKit::Menu:CMS', Newsletters: 'CmsKit::NewsletterUsers', Polls: 'CmsKit::Polls', Faqs: 'CmsKit::Faqs', PageFeedbacks: 'CmsKit::PageFeedbacks', UrlShorting: 'CmsKit::UrlForwarding', } as const; const CmsKitProPolicyNames = { Cms: 'CmsKit.Newsletter || CmsKit.Poll || CmsKit.Faq || CmsKit.PageFeedback || CmsKit.UrlShorting', Newsletters: 'CmsKit.Newsletter', Polls: 'CmsKit.Poll', Faqs: 'CmsKit.Faq', PageFeedbacks: 'CmsKit.PageFeedback', UrlShorting: 'CmsKit.UrlShorting', } as const; export function provideCmsKitAdminRoutesLight(): EnvironmentProviders { return makeEnvironmentProviders([ provideAppInitializer(() => { const routes = inject(RoutesService); routes.add([ { path: '/cms/blog-posts', name: CmsKitRouteNames.BlogPosts, parentName: CmsKitRouteNames.Cms, order: 1, layout: eLayoutType.application, iconClass: 'fa fa-file-alt', requiredPolicy: CmsKitPolicyNames.BlogPosts }, { path: '/cms/blogs', name: CmsKitRouteNames.Blogs, parentName: CmsKitRouteNames.Cms, order: 2, layout: eLayoutType.application, iconClass: 'fa fa-blog', requiredPolicy: CmsKitPolicyNames.Blogs }, { path: '/cms/comments', name: CmsKitRouteNames.Comments, parentName: CmsKitRouteNames.Cms, order: 3, layout: eLayoutType.application, iconClass: 'fa fa-comments', requiredPolicy: CmsKitPolicyNames.Comments }, { path: '/cms/global-resources', name: CmsKitRouteNames.GlobalResources, parentName: CmsKitRouteNames.Cms, order: 5, layout: eLayoutType.application, iconClass: 'fa fa-globe', requiredPolicy: CmsKitPolicyNames.GlobalResources }, { path: '/cms/menus', name: CmsKitRouteNames.Menus, parentName: CmsKitRouteNames.Cms, order: 6, layout: eLayoutType.application, iconClass: 'fa fa-bars', requiredPolicy: CmsKitPolicyNames.Menus }, { path: '/cms/pages', name: CmsKitRouteNames.Pages, parentName: CmsKitRouteNames.Cms, order: 9, layout: eLayoutType.application, iconClass: 'fa fa-file', requiredPolicy: CmsKitPolicyNames.Pages }, { path: '/cms/tags', name: CmsKitRouteNames.Tags, parentName: CmsKitRouteNames.Cms, order: 11, layout: eLayoutType.application, iconClass: 'fa fa-tags', requiredPolicy: CmsKitPolicyNames.Tags }, ]); }), ]); } export function provideCmsKitProAdminRoutesLight(): EnvironmentProviders { return makeEnvironmentProviders([ provideAppInitializer(() => { const routes = inject(RoutesService); routes.add([ { path: '/cms', name: CmsKitProRouteNames.Cms, order: 100, layout: eLayoutType.application, iconClass: 'fa fa-newspaper', requiredPolicy: CmsKitProPolicyNames.Cms }, { path: '/cms/faqs', name: CmsKitProRouteNames.Faqs, parentName: CmsKitProRouteNames.Cms, order: 4, layout: eLayoutType.application, iconClass: 'fa fa-question-circle', requiredPolicy: CmsKitProPolicyNames.Faqs }, { path: '/cms/newsletters', name: CmsKitProRouteNames.Newsletters, parentName: CmsKitProRouteNames.Cms, order: 7, layout: eLayoutType.application, iconClass: 'fa fa-newspaper', requiredPolicy: CmsKitProPolicyNames.Newsletters }, { path: '/cms/page-feedbacks', name: CmsKitProRouteNames.PageFeedbacks, parentName: CmsKitProRouteNames.Cms, order: 8, layout: eLayoutType.application, iconClass: 'fa fa-comment', requiredPolicy: CmsKitProPolicyNames.PageFeedbacks }, { path: '/cms/polls', name: CmsKitProRouteNames.Polls, parentName: CmsKitProRouteNames.Cms, order: 10, layout: eLayoutType.application, iconClass: 'fa fa-poll', requiredPolicy: CmsKitProPolicyNames.Polls }, { path: '/cms/url-shorting', name: CmsKitProRouteNames.UrlShorting, parentName: CmsKitProRouteNames.Cms, order: 12, layout: eLayoutType.application, iconClass: 'fa fa-forward', requiredPolicy: CmsKitProPolicyNames.UrlShorting }, ]); }), ]); }A few things to keep in mind when wiring this up:
- Do not
importanything from@abp/ng.cms-kit/admin/configor@volo/abp.ng.cms-kit-pro/admin/configin this file (or anywhere else loaded eagerly). The moment youimportany symbol from those entries — even the enum — the whole fesm comes in and bringsCmsSettingsComponent(and the heavy admin entry) with it. - Only the Pro provider above registers the
/cmsparent menu node (the OSS provider only registers child items hanging offparentName: 'CmsKit::Menu:CMS'). If you ever stop callingprovideCmsKitProAdminRoutesLight(), add an equivalent top-level entry yourself, otherwise the OSS children won't have a parent to attach to. BothCmsnames resolve to the same key, so the two route sets render as a single "CMS" group when both are registered. - You lose the "Settings → CMS Kit" tab in both modules. The OSS tab renders the
Require comment approvaltoggle; the Pro tab rendersContactsettings andPage Feedbacksettings. The easiest replacement is to expose the switches you actually use from your own settings page, or call the underlying APIs (CommentAdminService.updateSettingsfrom@abp/ng.cms-kit/proxy,ContactSettingService.updateandPageFeedbackAdminService.updateSettingsfrom@volo/abp.ng.cms-kit-pro/proxy) directly. - Keep your existing
loadChildrenfor@abp/ng.cms-kit/admin/@volo/abp.ng.cms-kit-pro/admin— those still serve the actual pages lazily.
In a stock 10.4.1 + Angular 21.2 +
@angular/buildproject this brings the production initial bundle from 3.13 MB / 738 kB transfer down to 1.84 MB / 424 kB transfer (−1.29 MB / −42%), with the 940 kBtoastuichunk moving from initial to lazy.Thanks
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) - Do not
-
0
thanks! We'll try that
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)