Repository: angular/angular-cli
File: packages/angular/build/src/tools/vite/plugins/setup-middlewares-plugin.ts (TypeScript)

Code before:
  assets: AngularOutputAssets;
  extensionMiddleware?: Connect.NextHandleFunction[];
  indexHtmlTransformer?: (content: string) => Promise<string>;
  componentStyles: Map<string, ComponentStyleRecord>;
  templateUpdates: Map<string, string>;
  ssrMode: ServerSsrMode;
  resetComponentUpdates: () => void;
  projectRoot: string;
  allowedHosts: true | string[];
  devHost: string;
}

function extractHostname(hostHeader: string | undefined): string {
  if (!hostHeader) {
    return '';
  }

  // Remove port if present (e.g., example.com:4200)
  const idx = hostHeader.lastIndexOf(':');
  if (idx > -1 && hostHeader.indexOf(']') === -1) {
    // Skip IPv6 addresses that include ':' within brackets
    return hostHeader.slice(0, idx).toLowerCase();
  }

  return hostHeader.toLowerCase();
}

function html403(hostname: string): string {
  return `<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Blocked request</title>
    <style>
      body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif;
      line-height:1.4;margin:2rem;color:#1f2937}
      code{background:#f3f4f6;padding:.15rem .35rem;border-radius:.25rem}
      .box{max-width:760px;margin:0 auto}
      h1{font-size:1.5rem;margin-bottom:.75rem}
      p{margin:.5rem 0}
      .muted{color:#6b7280}
      pre{background:#f9fafb;border:1px solid #e5e7eb;padding:.75rem;border-radius:.5rem;overflow:auto}
    </style>
  </head>
  <body>
    <main>
      <h1>Blocked request. This host ("${hostname}") is not allowed.</h1>
      <p>To allow this host, add it to <code>allowedHosts</code> under the <code>serve</code> target in <code>angular.json</code>.</p>
      <pre><code>{
  "serve": {

Review comment (suggestion):
This cannot be IPV6 if the hostname is IPV6 it will not error.

I suggest to remove the function altogether and do

```suggestion
const hostname = req.headers.host?.split(':')[0].toLowerCase() ?? ''
```

Code after:
  outputFiles: AngularMemoryOutputFiles;
  assets: AngularOutputAssets;
  extensionMiddleware?: Connect.NextHandleFunction[];
  indexHtmlTransformer?: (content: string) => Promise<string>;
  componentStyles: Map<string, ComponentStyleRecord>;
  templateUpdates: Map<string, string>;
  ssrMode: ServerSsrMode;
  resetComponentUpdates: () => void;
  projectRoot: string;
}

async function createEncapsulateStyle(): Promise<
  (style: Uint8Array, componentId: string) => string
> {
  const { encapsulateStyle } = await import('@angular/compiler');
  const decoder = new TextDecoder('utf-8');

  return (style, componentId) => {
    return encapsulateStyle(decoder.decode(style), componentId);
  };
}

export function createAngularSetupMiddlewaresPlugin(
  options: AngularSetupMiddlewaresPluginOptions,
): Plugin {
  return {
    name: 'vite:angular-setup-middlewares',
    enforce: 'pre',
    async configureServer(server) {
      const {
        indexHtmlTransformer,
        outputFiles,
        extensionMiddleware,
        assets,
        componentStyles,
        templateUpdates,
        ssrMode,
        resetComponentUpdates,
      } = options;

      // Headers, assets and resources get handled first
      server.middlewares.use(createAngularHeadersMiddleware(server));
      server.middlewares.use(createAngularComponentMiddleware(server, templateUpdates));
      server.middlewares.use(
        createAngularAssetsMiddleware(
          server,
          assets,
          outputFiles,
          componentStyles,
          await createEncapsulateStyle(),
        ),