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’s serve or build --configuration development) → true

  • webpack --mode production (Angular’s build --configuration production) → false

  • Override with environment.jsDEBUG_INFO_ENABLED: true to 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();
}

3.5. Source maps

Angular CLI controls source-map generation via angular.json build options per configuration. Dev builds include full source maps; prod builds emit hidden source maps (shipped only if sourceMap.hidden=true). Nothing to tie to DEBUG_INFO_ENABLED.

3.6. Minification

Controlled by Angular CLI’s optimization.scripts / optimization.styles per build configuration. Dev = unminified; prod = minified. Again, separate from the flag.

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:

  1. Create packages/shared-ui/src/dev-banner/dev-banner.component.ts.

  2. Read DEBUG_INFO_ENABLED at init; show nothing when false (compiler dead-code-eliminates the entire branch in prod builds).

  3. Show the version (VERSION) and, optionally, a short env label (environment.envLabel — "dev" / "stage" / "prod").

  4. 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 DefinePlugin with DEBUG_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-banner in the app shell from the first commit.

  • Version label should distinguish admin-portal builds from registration-portal builds (env var APP_NAME=admin-portal can drive the banner text).

7. Reference

File Role

registration-portal/webpack/webpack.custom.js

DefinePlugin injection for DEBUG_INFO_ENABLED, VERSION, etc.

registration-portal/webpack/environment.js

Base values for the injected constants

registration-portal/webpack/proxy.conf.js

Dev-server proxy rules

registration-portal/src/typings.d.ts

TypeScript declarations for the global constants

@ems/shared-ui — planned

Home of the DevBannerComponent once the library lands (WS4)

9. Change History

Date Change

2026-04-24

Initial draft. Captures webpack DefinePlugin pattern (grounded in registration-portal webpack.custom.js) and proposes a DevBannerComponent for @ems/shared-ui.