in modules/builders/src/ssr-dev-server/index.ts [202:308]
async function initBrowserSync(
browserSyncInstance: browserSync.BrowserSyncInstance,
nodeServerPort: number,
options: SSRDevServerBuilderOptions,
context: BuilderContext,
): Promise<browserSync.BrowserSyncInstance> {
if (browserSyncInstance.active) {
return browserSyncInstance;
}
const { port: browserSyncPort, open, host, publicHost, proxyConfig } = options;
const bsPort = browserSyncPort || (await getAvailablePort());
const bsOptions: browserSync.Options = {
proxy: {
target: `localhost:${nodeServerPort}`,
proxyOptions: {
xfwd: true,
},
proxyRes: [
(proxyRes) => {
if ('headers' in proxyRes) {
proxyRes.headers['cache-control'] = undefined;
}
},
],
// proxyOptions is not in the typings
} as browserSync.ProxyOptions & { proxyOptions: { xfwd: boolean } },
host,
port: bsPort,
ui: false,
server: false,
notify: false,
ghostMode: false,
logLevel: 'silent',
open,
https: getSslConfig(context.workspaceRoot, options),
};
const publicHostNormalized =
publicHost && publicHost.endsWith('/')
? publicHost.substring(0, publicHost.length - 1)
: publicHost;
if (publicHostNormalized) {
const { protocol, hostname, port, pathname } = url.parse(publicHostNormalized);
const defaultSocketIoPath = '/browser-sync/socket.io';
const defaultNamespace = '/browser-sync';
const hasPathname = !!(pathname && pathname !== '/');
const namespace = hasPathname ? pathname + defaultNamespace : defaultNamespace;
const path = hasPathname ? pathname + defaultSocketIoPath : defaultSocketIoPath;
bsOptions.socket = {
namespace,
path,
domain: url.format({
protocol,
hostname,
port,
}),
};
// When having a pathname we also need to create a reverse proxy because socket.io
// will be listening on: 'http://localhost:4200/ssr/browser-sync/socket.io'
// However users will typically have a reverse proxy that will redirect all matching requests
// ex: http://testinghost.com/ssr -> http://localhost:4200 which will result in a 404.
if (hasPathname) {
// Remove leading slash
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
(bsOptions.scriptPath = (p) => p.substring(1)),
(bsOptions.middleware = [
createProxyMiddleware(defaultSocketIoPath, {
target: url.format({
protocol: 'http',
hostname: host,
port: bsPort,
pathname: path,
}),
ws: true,
logLevel: 'silent',
}) as any,
]);
}
}
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) {
reject(error);
} else {
resolve(bs);
}
});
});
}