Dev-Mode UI Awareness
1. Overview
An Angular SPA built for development and one built for production should look and behave visibly differently. Developers want source maps, un-minified bundles, verbose logs, a visible "this is dev" banner. Operators — and customers — want the opposite. EMS portals use webpack’s DefinePlugin to inject a build-time flag (DEBUG_INFO_ENABLED) that the SPA reads and branches on.
This page documents the mechanism, what the flag gates, and what every portal (admin-portal, membership-ui, registration-portal) should carry forward.
See also: Portal Pattern.
2. The Flag
DEBUG_INFO_ENABLED is a global boolean injected at build time by webpack:
// registration-portal/webpack/webpack.custom.js (lines 116-127)
config.plugins.push(
new webpack.DefinePlugin({
I18N_HASH: JSON.stringify(languagesHash.hash),
__VERSION__: JSON.stringify(environment.__VERSION__),
__DEBUG_INFO_ENABLED__: environment.__DEBUG_INFO_ENABLED__ || config.mode === 'development',
SERVER_API_URL: JSON.stringify(environment.SERVER_API_URL),
}),
);
Value:
-
webpack --mode development(Angular’sserveorbuild --configuration development) →true -
webpack --mode production(Angular’sbuild --configuration production) →false -
Override with
environment.js→DEBUG_INFO_ENABLED: trueto force debug-on in a production build (useful when testing a staging promotion candidate)
Reference: [registration-portal/webpack/webpack.custom.js](registration-portal/webpack/webpack.custom.js).
TypeScript declaration lives in src/typings.d.ts:
declare const __DEBUG_INFO_ENABLED__: boolean;
declare const __VERSION__: string;
declare const I18N_HASH: string;
declare const SERVER_API_URL: string;
3. What the Flag Gates
Recommended behaviours — some already implemented in registration-portal, some worth adding in admin-portal:
3.1. Visible dev banner
A persistent strip at the top of the viewport saying e.g. "DEV BUILD — <version>" in a distinctive colour. Prevents the too-common "is this prod?" confusion during demos and incident response.
registration-portal does not currently have this component. Adding it is a small, high-value improvement — add to @ems/shared-ui so all portals get it.
Sketch:
@Component({
selector: 'ems-dev-banner',
template: `
<div *ngIf="show" class="ems-dev-banner">
DEV BUILD — {{ version }} — commit {{ commit }}
</div>
`,
})
export class DevBannerComponent {
show = __DEBUG_INFO_ENABLED__;
version = __VERSION__;
commit = /* injected via DefinePlugin if desired */;
}
Place in the app shell template above the router outlet.
3.2. Verbose console logging
Application-level loggers (not framework loggers) branch on the flag:
export function debugLog(message: string, ...args: unknown[]) {
if (__DEBUG_INFO_ENABLED__) {
console.debug(`[ems] ${message}`, ...args);
}
}
Production builds strip the call entirely when bundled with Terser (the console.debug wrapped in a dead branch gets removed as unreachable code).
3.3. Interceptor logging
HTTP interceptors log full request/response details when the flag is true; log only errors otherwise. Already partially present in registration-portal’s ErrorHandlerInterceptor; extend consistently.
3.4. Angular enableProdMode()
Not gated by DEBUG_INFO_ENABLED — gated by environment.production which is set per Angular build configuration. Overlapping but distinct mechanisms; both stay.
// main.ts
if (environment.production) {
enableProdMode();
}
4. Dev Server and BrowserSync
registration-portal’s dev setup is slightly non-standard for JHipster 8 — environment.ts / environment.prod.ts are not used; instead webpack’s DefinePlugin injects constants:
// webpack.custom.js dev-server settings
if (config.devServer) {
config.devServer.proxy = proxyConfig({ tls });
config.devServer.allowedHosts = 'all'; // multi-tenant dev: hosts like tenant-a.localhost
}
if (targetOptions.target === 'serve' || config.watch) {
config.plugins.push(
new BrowserSyncPlugin({
host: '0.0.0.0',
port: 9000,
proxy: {
target: `http://localhost:${targetOptions.target === 'serve' ? '4200' : '12505'}`,
ws: true,
},
cors: true,
}),
);
}
Ports:
-
Angular
ng serve: 4200 -
Spring Boot portal (dev): 12505 (registration-portal), 12506 (admin-portal)
-
BrowserSync: 9000 (multi-tenant-friendly, allowedHosts=all)
Developers using npm start typically hit 9000 (BrowserSync) or 4200 (direct Angular). Both proxy admin-service at /services/admin-service/** via the Spring Boot portal; the portal itself must be running.
allowedHosts: 'all' matters for testing multi-tenant resolution locally — hosts like tenant-a.localhost and tenant-b.localhost (configured in /etc/hosts as 127.0.0.1) are all accepted by the dev server.
5. Adding a Dev Banner to @ems/shared-ui
This is the concrete WS4 add:
-
Create
packages/shared-ui/src/dev-banner/dev-banner.component.ts. -
Read
DEBUG_INFO_ENABLEDat init; show nothing whenfalse(compiler dead-code-eliminates the entire branch in prod builds). -
Show the version (
VERSION) and, optionally, a short env label (environment.envLabel— "dev" / "stage" / "prod"). -
Ship with default styling (distinctive bar, high-contrast text) that consumers can override via CSS vars.
Publish once with the first @ems/shared-ui release; admin-portal includes it in its app shell from day one.
6. admin-portal Notes
admin-portal inherits the webpack setup from registration-portal:
-
Same
DefinePluginwithDEBUG_INFO_ENABLED,VERSION,I18N_HASH,SERVER_API_URL. -
Port 12506 (portal) + 9000 (BrowserSync) + 4200 (ng serve).
-
allowedHosts: 'all'for multi-tenant local testing. -
ems-dev-bannerin the app shell from the first commit. -
Version label should distinguish admin-portal builds from registration-portal builds (env var
APP_NAME=admin-portalcan drive the banner text).
7. Reference
| File | Role |
|---|---|
|
DefinePlugin injection for |
|
Base values for the injected constants |
|
Dev-server proxy rules |
|
TypeScript declarations for the global constants |
|
Home of the |