Skip to content

Commit

Permalink
feat(@nguniversal/builders): add support for proxy configuration in s…
Browse files Browse the repository at this point in the history
…sr-dev-server

With this change we added a new `proxyConfig` option to be used to provide custom proxy configurations to the ssr-dev-server builder.

Closes #1757
  • Loading branch information
alan-agius4 committed Apr 19, 2021
1 parent a80f4c9 commit 6ef0de8
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 1 deletion.
34 changes: 34 additions & 0 deletions modules/builders/src/ssr-dev-server/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import { Architect, BuilderRun } from '@angular-devkit/architect';
import * as browserSync from 'browser-sync';
import * as http from 'http';
import * as https from 'https';
import { from, throwError, timer } from 'rxjs';
import { concatMap, debounceTime, mergeMap, retryWhen, take } from 'rxjs/operators';
Expand Down Expand Up @@ -137,4 +138,37 @@ describe('Serve SSR Builder', () => {
)
.toPromise();
});

it('proxies requests based on the proxy configuration file provided in the option', async () => {
const proxyServer = http.createServer((request, response) => {
if (request.url?.endsWith('/test')) {
response.writeHead(200);
response.end('TEST_API_RETURN');
} else {
response.writeHead(404);
response.end();
}
});

try {
await new Promise<void>(resolve => proxyServer.listen(0, '127.0.0.1', resolve));
const proxyAddress = proxyServer.address() as import('net').AddressInfo;

host.writeMultipleFiles({
'proxy.config.json': `{ "/api/*": { "logLevel": "debug","target": "http://127.0.0.1:${proxyAddress.port}" } }`,
});

const run = await architect.scheduleTarget(target, { port: 7001, proxyConfig: 'proxy.config.json' });
runs.push(run);

const output = await run.result as SSRDevServerBuilderOutput;
expect(output.success).toBe(true);
expect(output.baseUrl).toBe('http://localhost:7001');
const response = await fetch('http://localhost:7001/api/test');
expect(await response?.text()).toContain('TEST_API_RETURN');

} finally {
await new Promise<void>((resolve) => proxyServer.close(() => resolve()));
}
});
});
48 changes: 47 additions & 1 deletion modules/builders/src/ssr-dev-server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from '@angular-devkit/architect';
import { json, logging, tags } from '@angular-devkit/core';
import * as browserSync from 'browser-sync';
import { existsSync } from 'fs';
import { createProxyMiddleware } from 'http-proxy-middleware';
import { join, resolve as pathResolve } from 'path';
import {
Expand Down Expand Up @@ -212,7 +213,7 @@ async function initBrowserSync(
return browserSyncInstance;
}

const { port: browserSyncPort, open, host, publicHost } = options;
const { port: browserSyncPort, open, host, publicHost, proxyConfig } = options;
const bsPort = browserSyncPort || await getAvailablePort();
const bsOptions: browserSync.Options = {
proxy: {
Expand Down Expand Up @@ -284,6 +285,19 @@ async function initBrowserSync(
}
}

if (proxyConfig) {
if (!bsOptions.middleware) {
bsOptions.middleware = [];
} else if (!Array.isArray(bsOptions.middleware)) {
bsOptions.middleware = [bsOptions.middleware];
}

bsOptions.middleware = [
...bsOptions.middleware,
...getProxyConfig(context.workspaceRoot, proxyConfig),
];
}

return new Promise((resolve, reject) => {
browserSyncInstance.init(bsOptions, (error, bs) => {
if (error) {
Expand Down Expand Up @@ -322,4 +336,36 @@ function getSslConfig(
return ssl;
}

function getProxyConfig(
root: string,
proxyConfig: string,
): browserSync.MiddlewareHandler[] {
const proxyPath = pathResolve(root, proxyConfig);
let proxySettings: any;
try {
proxySettings = require(proxyPath);
} catch (error) {
if (error.code === 'MODULE_NOT_FOUND') {
throw new Error(`Proxy config file ${proxyPath} does not exist.`);
}

throw error;
}

const proxies = Array.isArray(proxySettings) ? proxySettings : [proxySettings];

return proxies.map(proxy => {
const keys = Object.keys(proxy);
const context = keys[0];

if (keys.length === 1 || typeof context === 'string') {
const normalizedContext = context.replace(/^\*$/, '**').replace(/\/\*$/, '');

return createProxyMiddleware(normalizedContext, proxy[context]) as any;
}

return createProxyMiddleware(proxy) as any;
});
}

export default createBuilder<SSRDevServerBuilderOptions, BuilderOutput>(execute);
4 changes: 4 additions & 0 deletions modules/builders/src/ssr-dev-server/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@
"sslCert": {
"type": "string",
"description": "SSL certificate to use for serving HTTPS."
},
"proxyConfig": {
"type": "string",
"description": "Proxy configuration file."
}
},
"additionalProperties": false,
Expand Down
3 changes: 3 additions & 0 deletions modules/builders/src/ssr-dev-server/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,7 @@ export interface Schema {

/** SSL certificate to use for serving HTTPS. */
sslCert?: string;

/** Proxy configuration file */
proxyConfig?: string;
}
1 change: 1 addition & 0 deletions tools/defaults.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ def jasmine_node_test(deps = [], **kwargs):

_jasmine_node_test(
deps = local_deps,
templated_args = ["--bazel_patch_module_resolver"],
configuration_env_vars = ["compile"],
**kwargs
)
Expand Down

0 comments on commit 6ef0de8

Please sign in to comment.